From 8a30e338692921b78e5e6aefdabde51af9f0c1d6 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 11 Jan 2025 12:29:39 -0500 Subject: [PATCH 01/83] Initial MacOS port. Needs manual codesign and dma doesn't work yet. --- CMakeLists.txt | 5 ++ example/CMakeLists.txt | 6 +- example/thunderscope-user.entitlements | 10 +++ example/thunderscope_test.cpp | 2 +- litepcie/CMakeLists.txt | 8 +- litepcie/include/litepcie_dma.h | 15 ++-- litepcie/include/litepcie_helpers.h | 14 +++ litepcie/public_h/litepcie.h | 6 +- litepcie/public_h/litepcie_mac.h | 113 +++++++++++++++++++++++++ litepcie/src/litepcie_dma.c | 64 +++++++++----- litepcie/src/litepcie_helpers.c | 85 +++++++++++++++++++ src/samples.c | 68 ++++++++++++--- src/samples.h | 2 +- src/util.h | 4 +- 14 files changed, 358 insertions(+), 44 deletions(-) create mode 100644 example/thunderscope-user.entitlements create mode 100644 litepcie/public_h/litepcie_mac.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 53781f8..d038d83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,11 @@ set_target_properties(tslitex PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) target_link_libraries(tslitex litepcie) + +if(APPLE) + target_link_libraries(tslitex "-framework IOKit") +endif() + target_include_directories(tslitex PUBLIC include) #### diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index da522c6..c8c2308 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -4,16 +4,20 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/example") add_executable(thunderscope_test thunderscope_test.cpp) target_link_libraries(thunderscope_test tslitex_static) - +target_link_libraries(thunderscope_test "-framework IOKit") if(WIN32) target_link_libraries(thunderscope_test setupapi) +elif(APPLE) endif() + # Thunderscope FW Update CLI add_executable(thunderscope_fw thunderscope_fw.cpp) target_link_libraries(thunderscope_fw tslitex_static) +target_link_libraries(thunderscope_fw "-framework IOKit") if(WIN32) target_link_libraries(thunderscope_fw setupapi) +elif(APPLE) endif() diff --git a/example/thunderscope-user.entitlements b/example/thunderscope-user.entitlements new file mode 100644 index 0000000..8b0674b --- /dev/null +++ b/example/thunderscope-user.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.driverkit.userclient-access + + litex.litepcie + + + diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index f4377f5..7080768 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -159,7 +159,7 @@ static void test_io(file_t fd) /* Write to scratch register. */ printf("Write 0x12345678 to Scratch register:\n"); - litepcie_writel(fd, CSR_CTRL_SCRATCH_ADDR, 0x0); + litepcie_writel(fd, CSR_CTRL_SCRATCH_ADDR, 0x12345678); printf("Read: 0x%08x\n", litepcie_readl(fd, CSR_CTRL_SCRATCH_ADDR)); /* Read from scratch register. */ diff --git a/litepcie/CMakeLists.txt b/litepcie/CMakeLists.txt index 1ec593f..b083a97 100644 --- a/litepcie/CMakeLists.txt +++ b/litepcie/CMakeLists.txt @@ -11,10 +11,14 @@ if(WIN32) set(litepcie_PLATFORM_HEADERS public_h/litepcie_win.h ) -else() +elif(LINUX) set(litepcie_PLATFORM_HEADERS public_h/litepcie_linux.h ) +elif(APPLE) +set(litepcie_PLATFORM_HEADERS + public_h/litepcie_mac.h + ) endif() set(litepcie_HEADERS @@ -45,4 +49,6 @@ target_include_directories(litepcie PUBLIC public_h include) if(WIN32) target_link_libraries(litepcie setupapi) +elif(APPLE) + target_link_libraries(tslitex "-framework IOKit") endif() \ No newline at end of file diff --git a/litepcie/include/litepcie_dma.h b/litepcie/include/litepcie_dma.h index 4cdc7cf..57167ca 100644 --- a/litepcie/include/litepcie_dma.h +++ b/litepcie/include/litepcie_dma.h @@ -16,7 +16,7 @@ #include "litepcie_helpers.h" #include "litepcie.h" -#if defined(_WIN32) +#if !defined(__LINUX__) typedef struct pollfd_s { file_t fd; @@ -30,7 +30,8 @@ struct litepcie_dma_ctrl { uint8_t use_reader, use_writer, loopback, zero_copy; pollfd_t fds; uint32_t channel; - char *buf_rd, *buf_wr; + uint8_t *buf_rd, *buf_wr; + size_t buf_wr_size, buf_rd_size; int64_t reader_hw_count, reader_sw_count, reader_dropped_count; int64_t writer_hw_count, writer_sw_count, writer_dropped_count; unsigned buffers_available_read, buffers_available_write; @@ -39,12 +40,12 @@ struct litepcie_dma_ctrl { struct litepcie_ioctl_mmap_dma_update mmap_dma_update; }; -void litepcie_dma_set_loopback(file_t fd, uint8_t loopback_enable); -void litepcie_dma_reader(file_t fd, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); -void litepcie_dma_writer(file_t fd, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); +void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_enable); +void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); +void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); -uint8_t litepcie_request_dma(file_t fd, uint8_t reader, uint8_t writer); -void litepcie_release_dma(file_t fd, uint8_t reader, uint8_t writer); +uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer); +void litepcie_release_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer); int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, uint8_t zero_copy); void litepcie_dma_cleanup(struct litepcie_dma_ctrl *dma); diff --git a/litepcie/include/litepcie_helpers.h b/litepcie/include/litepcie_helpers.h index 86a17eb..56e8bc9 100644 --- a/litepcie/include/litepcie_helpers.h +++ b/litepcie/include/litepcie_helpers.h @@ -26,6 +26,20 @@ void _check_ioctl(int status, const char* file, int line); #define LITEPCIE_CTRL_NAME(ID) "\\CTRL" #ID #define LITEPCIE_DMA_NAME(ID, CHAN) "\\DMA" #ID #CHAN +#elif defined(__APPLE__) +#include +#include +#include +#include +typedef io_connect_t file_t; + +#define ioctl_args(fd, op, data) fd, op, &(data), (size_t)sizeof(data), &(data), &outlen +#define checked_ioctl(...) _check_ioctl((int)!IOConnectCallStructMethod(__VA_ARGS__), __FILE__, __LINE__) +void _check_ioctl(int status, const char* file, int line); + +//TODO: Add support for multiple TS +#define LITEPCIE_CTRL_NAME(ID) "litepcie" +#define LITEPCIE_DMA_NAME(ID, CHAN) "litepcie" #else #include typedef int file_t; diff --git a/litepcie/public_h/litepcie.h b/litepcie/public_h/litepcie.h index ed28e42..db90f73 100644 --- a/litepcie/public_h/litepcie.h +++ b/litepcie/public_h/litepcie.h @@ -13,8 +13,12 @@ extern "C" { #ifdef _WIN32 #include "litepcie_win.h" -#else +#elif __LINUX__ #include "litepcie_linux.h" +#elif __APPLE__ +#include "litepcie_mac.h" +#else +#error "UNSUPPORTED PLATFORM" #endif #ifdef __cplusplus diff --git a/litepcie/public_h/litepcie_mac.h b/litepcie/public_h/litepcie_mac.h new file mode 100644 index 0000000..42609fc --- /dev/null +++ b/litepcie/public_h/litepcie_mac.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * LitePCIe driver + * + * This file is part of LitePCIe. + * + * Copyright (C) 2018-2023 / EnjoyDigital / florent@enjoy-digital.fr + * + */ + +#ifndef _MAC_LITEPCIE_H +#define _MAC_LITEPCIE_H + +#include + +#include "csr.h" +#include "config.h" + + +struct litepcie_ioctl_dma { + uint8_t loopback_enable; + uint8_t channel; +} __attribute__((packed)); + +struct litepcie_ioctl_dma_writer { + uint8_t enable; + uint8_t channel; + int64_t hw_count; + int64_t sw_count; + int64_t lost_count; +} __attribute__((packed)); + +struct litepcie_ioctl_dma_reader { + uint8_t enable; + uint8_t channel; + int64_t hw_count; + int64_t sw_count; + int64_t lost_count; +} __attribute__((packed)); + +struct litepcie_ioctl_lock { + uint8_t dma_reader_request; + uint8_t dma_writer_request; + uint8_t dma_reader_release; + uint8_t dma_writer_release; + uint8_t dma_reader_status; + uint8_t dma_writer_status; +}__attribute__((packed)); + +struct litepcie_ioctl_mmap_dma_info { + uint64_t dma_tx_buf_offset; + uint64_t dma_tx_buf_size; + uint64_t dma_tx_buf_count; + + uint64_t dma_rx_buf_offset; + uint64_t dma_rx_buf_size; + uint64_t dma_rx_buf_count; +}; + +struct litepcie_ioctl_mmap_dma_update { + int64_t sw_count; +}; + +enum LitePCIeMessageType { + LITEPCIE_CONFIG_DMA_READER_CHANNEL, + LITEPCIE_CONFIG_DMA_WRITER_CHANNEL, + LITEPCIE_READ_CSR, + LITEPCIE_WRITE_CSR, + LITEPCIE_ICAP, + LITEPCIE_FLASH, + LITEPCIE_CONFIG_DMA, + LITEPCIE_CONFIG_DMA_LOCK +}; + +enum LitePCIeMemoryType { + LITEPCIE_DMA_READER = 0x00010000, + LITEPCIE_DMA_WRITER = 0x00020000, + LITEPCIE_DMA_COUNTS = 0x00040000, +}; + +typedef struct DMACounts { + uint64_t hwReaderCountTotal; + uint64_t hwReaderCountPrev; + uint64_t hwReaderLost; + uint64_t hwWriterCountTotal; + uint64_t hwWriterCountPrev; + uint64_t hwWriterLost; +} __attribute__((packed)) DMACounts; + +typedef struct litepcie_ioctl_flash { + uint32_t tx_len; /* 8 to 40 */ + uint64_t tx_data; /* 8 to 40 bits */ + uint64_t rx_data; /* 40 bits */ +} __attribute__((packed)) LitePCIeFlashCallData; + +typedef struct litepcie_ioctl_icap { + uint8_t addr; + uint32_t data; +} __attribute__((packed)) LitePCIeICAPCallData; + + +#define LITEPCIE_IOCTL_FLASH LITEPCIE_FLASH +#define LITEPCIE_IOCTL_ICAP LITEPCIE_ICAP + +#define LITEPCIE_IOCTL_DMA LITEPCIE_CONFIG_DMA +#define LITEPCIE_IOCTL_DMA_WRITER LITEPCIE_CONFIG_DMA_WRITER_CHANNEL +#define LITEPCIE_IOCTL_DMA_READER LITEPCIE_CONFIG_DMA_READER_CHANNEL +#define LITEPCIE_IOCTL_MMAP_DMA_INFO LITEPCIE_CONFIG_DMA_WRITER_CHANNEL +#define LITEPCIE_IOCTL_LOCK LITEPCIE_CONFIG_DMA_LOCK +// #define LITEPCIE_IOCTL_MMAP_DMA_WRITER_UPDATE //TBD +// #define LITEPCIE_IOCTL_MMAP_DMA_READER_UPDATE //TBD + +#endif /* _LINUX_LITEPCIE_H */ diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index 6dfb40b..e16b502 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -29,25 +29,40 @@ #include "litepcie_helpers.h" -void litepcie_dma_set_loopback(file_t fd, uint8_t loopback_enable) { +void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_enable) { struct litepcie_ioctl_dma m; m.loopback_enable = loopback_enable; - checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_DMA, m)); +#if defined(__APPLE__) + size_t outlen = sizeof(m); + m.channel = dma->channel; +#endif + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA, m)); } -void litepcie_dma_writer(file_t fd, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { +void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_writer m; m.enable = enable; - checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_DMA_WRITER, m)); +#if defined(__APPLE__) + size_t outlen = sizeof(m); + m.channel = dma->channel; + m.sw_count = *sw_count; +#endif + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, m)); + *hw_count = m.hw_count; *sw_count = m.sw_count; *lost_count = m.lost_count; } -void litepcie_dma_reader(file_t fd, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { +void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_reader m; m.enable = enable; - checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_DMA_READER, m)); +#if defined(__APPLE__) + size_t outlen = sizeof(m); + m.channel = dma->channel; + m.sw_count = *sw_count; +#endif + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_READER, m)); *hw_count = m.hw_count; *sw_count = m.sw_count; *lost_count = m.lost_count; @@ -55,23 +70,29 @@ void litepcie_dma_reader(file_t fd, uint8_t enable, int64_t *hw_count, int64_t * /* lock */ -uint8_t litepcie_request_dma(file_t fd, uint8_t reader, uint8_t writer) { +uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer) { struct litepcie_ioctl_lock m; m.dma_reader_request = reader > 0; m.dma_writer_request = writer > 0; m.dma_reader_release = 0; m.dma_writer_release = 0; - checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_LOCK, m)); +#if defined(__APPLE__) + size_t outlen = sizeof(m); +#endif + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_LOCK, m)); return m.dma_reader_status; } -void litepcie_release_dma(file_t fd, uint8_t reader, uint8_t writer) { +void litepcie_release_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer) { struct litepcie_ioctl_lock m; m.dma_reader_request = 0; m.dma_writer_request = 0; m.dma_reader_release = reader > 0; m.dma_writer_release = writer > 0; - checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_LOCK, m)); +#if defined(__APPLE__) + size_t outlen = sizeof(m); +#endif + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_LOCK, m)); } int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, uint8_t zero_copy) @@ -95,7 +116,7 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED); //Last char is channel ID. Zero remove char before opening file -#else +#elif defined(__LINUX__) if (dma->use_reader) dma->fds.events |= POLLOUT; if (dma->use_writer) @@ -104,18 +125,18 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui #endif dma->fds.fd = litepcie_open(devName, flags); - if (dma->fds.fd < 0) { + if (dma->fds.fd <= 0) { fprintf(stderr, "Could not open device\n"); return -1; } /* request dma reader and writer */ - if ((litepcie_request_dma(dma->fds.fd, dma->use_reader, dma->use_writer) == 0)) { + if ((litepcie_request_dma(dma, dma->use_reader, dma->use_writer) == 0)) { fprintf(stderr, "DMA not available\n"); return -1; } - litepcie_dma_set_loopback(dma->fds.fd, dma->loopback); + litepcie_dma_set_loopback(dma, dma->loopback); if (dma->zero_copy) { #if defined(_WIN32) @@ -123,6 +144,9 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui return -1; #else /* if mmap: get it from the kernel */ +#if defined(__APPLE__) + size_t outlen = sizeof(struct litepcie_ioctl_mmap_dma_info); +#endif checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_MMAP_DMA_INFO, dma->mmap_dma_info)); if (dma->use_writer) { dma->buf_rd = mmap(NULL, DMA_BUFFER_TOTAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, @@ -166,11 +190,11 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui void litepcie_dma_cleanup(struct litepcie_dma_ctrl *dma) { if (dma->use_reader) - litepcie_dma_reader(dma->fds.fd, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); + litepcie_dma_reader(dma, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); if (dma->use_writer) - litepcie_dma_writer(dma->fds.fd, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); + litepcie_dma_writer(dma, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); - litepcie_release_dma(dma->fds.fd, dma->use_reader, dma->use_writer); + litepcie_release_dma(dma, dma->use_reader, dma->use_writer); if (dma->zero_copy) { #if !defined(_WIN32) @@ -194,9 +218,9 @@ void litepcie_dma_process(struct litepcie_dma_ctrl *dma) /* set / get dma */ if (dma->use_writer) - litepcie_dma_writer(dma->fds.fd, 1, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); + litepcie_dma_writer(dma, 1, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); if (dma->use_reader) - litepcie_dma_reader(dma->fds.fd, 1, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); + litepcie_dma_reader(dma, 1, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); #if defined(_WIN32) uint32_t retLen = 0; @@ -287,6 +311,8 @@ void litepcie_dma_process(struct litepcie_dma_ctrl *dma) dma->usr_write_buf_offset = 0; } +#elif defined(__APPLE__) +//TODO #else /* polling */ retVal = poll(&dma->fds, 1, 100); diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 953ce17..5ae3f34 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -13,6 +13,10 @@ #include #include #include +#elif defined(__APPLE__) +#include +#include +#include #else #include #include @@ -81,30 +85,71 @@ static void getDeviceName(PWCHAR devName, DWORD maxLen, DWORD devIdx) SetupDiDestroyDeviceInfoList(hwDevInfo); return; } +#elif defined(__APPLE__) + +void _print_kerr_details(kern_return_t ret) +{ + printf("\tSystem: 0x%02x\n", err_get_system(ret)); + printf("\tSubsystem: 0x%03x\n", err_get_sub(ret)); + printf("\tCode: 0x%04x\n", err_get_code(ret)); +} #endif uint32_t litepcie_readl(file_t fd, uint32_t addr) { +#if defined(__APPLE__) + kern_return_t ret = kIOReturnSuccess; + + uint32_t olen = 1; + uint64_t output = 0; + uint64_t input = addr; + + ret = IOConnectCallScalarMethod(fd, LITEPCIE_READ_CSR, &input, 1, &output, &olen); + + if (ret != kIOReturnSuccess) { + printf("LITEPCIE_READ_CSR failed with error: 0x%08x.\n", ret); + _print_kerr_details(ret); + } + + return (uint32_t)output; +#else struct litepcie_ioctl_reg regData = { 0 }; regData.addr = addr; regData.is_write = 0; checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_REG, regData)); return regData.val; +#endif } void litepcie_writel(file_t fd, uint32_t addr, uint32_t val) { +#if defined(__APPLE__) + kern_return_t ret = kIOReturnSuccess; + + uint32_t olen = 0; + uint64_t input[2] = { addr, val }; + ret = IOConnectCallScalarMethod(fd, LITEPCIE_WRITE_CSR, input, 2, NULL, &olen); + + if (ret != kIOReturnSuccess) { + printf("LITEPCIE_WRITE_CSR failed with error: 0x%08x.\n", ret); + _print_kerr_details(ret); + } +#else struct litepcie_ioctl_reg regData; regData.addr = addr; regData.val = val; regData.is_write = 1; checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_REG, regData)); +#endif } void litepcie_reload(file_t fd) { struct litepcie_ioctl_icap m; m.addr = 0x4; m.data = 0xf; +#if defined(__APPLE__) + size_t outlen = sizeof(struct litepcie_ioctl_icap); +#endif checked_ioctl(ioctl_args(fd, LITEPCIE_IOCTL_ICAP, m)); } @@ -135,6 +180,44 @@ file_t litepcie_open(const char* name, int32_t flags) devName[devLen + strlen(name) - 1] = '\0'; fd = CreateFile(devName, (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, flags, NULL); +#elif defined(__APPLE__) +static const char* dextIdentifier = "litepcie"; + + kern_return_t ret = kIOReturnSuccess; + io_iterator_t iterator = IO_OBJECT_NULL; + io_service_t service = IO_OBJECT_NULL; + io_connect_t connection = IO_OBJECT_NULL; + fd = IO_OBJECT_NULL; + + /// - Tag: ClientApp_Connect + ret = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching(dextIdentifier), &iterator); + if (ret != kIOReturnSuccess) { + printf("Unable to find service for identifier with error: 0x%08x.\n", ret); + _print_kerr_details(ret); + } + else + { + printf("Searching for dext service...\n"); + while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { + // Open a connection to this user client as a server to that client, and store the instance in "service" + ret = IOServiceOpen(service, mach_task_self_, kIOHIDServerConnectType, &connection); + + if (ret == kIOReturnSuccess) { + printf("\tOpened service.\n"); + fd = connection; + break; + } else { + printf("\tFailed opening service with error: 0x%08x.\n", ret); + } + + IOObjectRelease(service); + } + } + IOObjectRelease(iterator); + + if (service == IO_OBJECT_NULL) { + printf("Failed to match to device.\n"); + } #else fd = open(name, flags); #endif @@ -145,6 +228,8 @@ void litepcie_close(file_t fd) { #if defined(_WIN32) CloseHandle(fd); +#elif defined(__APPLE__) + IOServiceClose(fd); #else close(fd); #endif diff --git a/src/samples.c b/src/samples.c index 4eb4344..72d34bc 100644 --- a/src/samples.c +++ b/src/samples.c @@ -36,7 +36,12 @@ #define TS_DMA_NAME_ARGS(chan, dev) (chan), (dev) #define TS_DMA_OS_FLAGS (FILE_ATTRIBUTE_NORMAL | \ FILE_FLAG_NO_BUFFERING) -#else +#elif defined(__APPLE__) +#define TS_DMA_NAME "%s" +#define TS_DMA_NAME_LEN (16) +#define TS_DMA_NAME_ARGS(chan, dev) "litepcie" +#define TS_DMA_OS_FLAGS (O_CLOEXEC) +#elif defined(__LINUX__) #define TS_DMA_NAME "/dev/thunderscope%u" #define TS_DMA_NAME_LEN (24) #define TS_DMA_NAME_ARGS(chan, dev) (dev) @@ -57,13 +62,26 @@ int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel) snprintf(devName, TS_DMA_NAME_LEN, TS_DMA_NAME, TS_DMA_NAME_ARGS(channel, devIdx)); - inst->dma = litepcie_open(devName, TS_DMA_OS_FLAGS); + inst->dma.fds.fd = litepcie_open(devName, TS_DMA_OS_FLAGS); + inst->dma.channel = channel; - if((INVALID_HANDLE_VALUE != inst->dma) && - litepcie_request_dma(inst->dma, 0, 1)) + if((INVALID_HANDLE_VALUE != inst->dma.fds.fd) && + litepcie_request_dma(&inst->dma, 0, 1)) { - litepcie_dma_set_loopback(inst->dma, 0); + litepcie_dma_set_loopback(&inst->dma, 0); retVal = TS_STATUS_OK; +#if defined(__APPLE__) + mach_vm_address_t writerAddress = 0; + mach_vm_size_t writerSize = 0; + + IOConnectMapMemory64(inst->dma.fds.fd, LITEPCIE_DMA_WRITER | 0, mach_task_self(), &writerAddress, &writerSize, kIOMapAnywhere); + + if (writerAddress != NULL) { + inst->dma.buf_rd = (uint8_t*)writerAddress; + } else { + printf("failed to acquire writer mapped buffer"); + } +#endif } //else, DMA Unavailable } @@ -81,7 +99,7 @@ int32_t samples_enable_set(sampleStream_t* inst, uint8_t en) inst->active = en; //Start/Stop DMA - litepcie_dma_writer(inst->dma, en, + litepcie_dma_writer(&inst->dma, en, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -105,7 +123,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ #if defined(_WIN32) uint32_t len = 0; - if (!ReadFile(inst->dma, sampleBuffer, bufferLen, &len, NULL)) + if (!ReadFile(inst->dma.fds.fd, sampleBuffer, bufferLen, &len, NULL)) { LOG_ERROR("Sample Read failed: %d", GetLastError()); LOG_ERROR("Sample Read args: 0x%p - 0x%lx - 0x%x", sampleBuffer, bufferLen, len); @@ -115,10 +133,31 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ { retVal = (int32_t)len; } +#elif defined(__APPLE__) + while(retVal < (int32_t)bufferLen) + { + litepcie_dma_writer(&inst->dma, inst->active, + &inst->dma_buffer_count, + &inst->driver_buffer_count, + &inst->dropped_buffer_count); + int64_t buff_available = inst->dma_buffer_count - inst->driver_buffer_count; + if(buff_available > (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ)) + { + inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ); + } + printf("Sample Get buffer %llu (%llu hw) (%d/%u)\r\n", inst->driver_buffer_count, inst->dma_buffer_count, retVal, bufferLen); + if(buff_available > 0) + { + memcpy(sampleBuffer, &inst->dma.buf_rd[(inst->driver_buffer_count % DMA_BUFFER_COUNT) * TS_SAMPLE_BUFFER_SIZE], TS_SAMPLE_BUFFER_SIZE); + inst->driver_buffer_count++; + retVal += TS_SAMPLE_BUFFER_SIZE; + } + NS_DELAY(250); + } #else while (retVal < bufferLen) { - int32_t readLen = (int32_t)read(inst->dma, &sampleBuffer[retVal], (bufferLen - (uint32_t)retVal)); + int32_t readLen = (int32_t)read(inst->dma.fds.fd, &sampleBuffer[retVal], (bufferLen - (uint32_t)retVal)); if(readLen < 0) { LOG_ERROR("Sample Read failed: %d", readLen); @@ -142,7 +181,7 @@ int32_t samples_update_status(sampleStream_t* inst) return TS_STATUS_ERROR; } - litepcie_dma_writer(inst->dma, inst->active, + litepcie_dma_writer(&inst->dma, inst->active, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -160,13 +199,18 @@ int32_t samples_teardown(sampleStream_t* inst) else { inst->active = 0; - litepcie_dma_writer(inst->dma, 0, + litepcie_dma_writer(&inst->dma, 0, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); +#if defined(__APPLE__) + IOConnectUnmapMemory(inst->dma.fds.fd, + LITEPCIE_DMA_WRITER | 0, mach_task_self(), + (mach_vm_address_t)inst->dma.buf_rd); +#endif - litepcie_release_dma(inst->dma, 0, 1); - litepcie_close(inst->dma); + litepcie_release_dma(&inst->dma, 0, 1); + litepcie_close(inst->dma.fds.fd); } return retVal; diff --git a/src/samples.h b/src/samples.h index 61a4658..18a6db7 100644 --- a/src/samples.h +++ b/src/samples.h @@ -27,7 +27,7 @@ extern "C" { typedef struct sampleStream_s { - file_t dma; + struct litepcie_dma_ctrl dma; int64_t dma_buffer_count; int64_t driver_buffer_count; int64_t dropped_buffer_count; diff --git a/src/util.h b/src/util.h index d63185d..4805439 100644 --- a/src/util.h +++ b/src/util.h @@ -17,7 +17,9 @@ extern "C" { #include -#if !defined(_WIN32) +#if defined(__APPLE__) +#define INVALID_HANDLE_VALUE (IO_OBJECT_NULL) +#elif defined(__LINUX__) #define INVALID_HANDLE_VALUE (-1) #endif From 0914962da90bfb260930cb22e39d0ead91fb6779 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 18 Jan 2025 12:15:38 -0500 Subject: [PATCH 02/83] Update to fix DMA. Needs to be cleaned up. --- litepcie/include/litepcie_helpers.h | 2 +- litepcie/public_h/litepcie_mac.h | 8 +++---- litepcie/src/litepcie_dma.c | 36 ++++++++++++++++++++++------- litepcie/src/litepcie_helpers.c | 3 +++ src/samples.c | 19 +++++++++------ src/samples.h | 2 +- src/thunderscope.c | 2 +- 7 files changed, 50 insertions(+), 22 deletions(-) diff --git a/litepcie/include/litepcie_helpers.h b/litepcie/include/litepcie_helpers.h index 56e8bc9..8b2b0d5 100644 --- a/litepcie/include/litepcie_helpers.h +++ b/litepcie/include/litepcie_helpers.h @@ -34,7 +34,7 @@ void _check_ioctl(int status, const char* file, int line); typedef io_connect_t file_t; #define ioctl_args(fd, op, data) fd, op, &(data), (size_t)sizeof(data), &(data), &outlen -#define checked_ioctl(...) _check_ioctl((int)!IOConnectCallStructMethod(__VA_ARGS__), __FILE__, __LINE__) +#define checked_ioctl(...) _check_ioctl((int)IOConnectCallStructMethod(__VA_ARGS__), __FILE__, __LINE__) void _check_ioctl(int status, const char* file, int line); //TODO: Add support for multiple TS diff --git a/litepcie/public_h/litepcie_mac.h b/litepcie/public_h/litepcie_mac.h index 42609fc..d3875a9 100644 --- a/litepcie/public_h/litepcie_mac.h +++ b/litepcie/public_h/litepcie_mac.h @@ -23,16 +23,16 @@ struct litepcie_ioctl_dma { } __attribute__((packed)); struct litepcie_ioctl_dma_writer { - uint8_t enable; - uint8_t channel; + uint32_t enable; + uint32_t channel; int64_t hw_count; int64_t sw_count; int64_t lost_count; } __attribute__((packed)); struct litepcie_ioctl_dma_reader { - uint8_t enable; - uint8_t channel; + uint32_t enable; + uint32_t channel; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index e16b502..0f33b22 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -40,32 +40,51 @@ void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_e } void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { - struct litepcie_ioctl_dma_writer m; + struct litepcie_ioctl_dma_writer m, n; m.enable = enable; #if defined(__APPLE__) - size_t outlen = sizeof(m); + size_t outlen = sizeof(n); m.channel = dma->channel; m.sw_count = *sw_count; -#endif + + IOConnectCallStructMethod(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, &m, sizeof(m), &n, &outlen); + + if(outlen != sizeof(n)) + { + printf(stderr, "IOCTL Output Len Invalid: %u, expected %u\r\n", outlen, sizeof(n)); + } + *hw_count = n.hw_count; + *sw_count = n.sw_count; + *lost_count = n.lost_count; +#else checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, m)); *hw_count = m.hw_count; *sw_count = m.sw_count; *lost_count = m.lost_count; +#endif } void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { - struct litepcie_ioctl_dma_reader m; + struct litepcie_ioctl_dma_reader m, n; m.enable = enable; #if defined(__APPLE__) - size_t outlen = sizeof(m); + size_t outlen = sizeof(n); m.channel = dma->channel; m.sw_count = *sw_count; -#endif - checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_READER, m)); + + IOConnectCallStructMethod(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, &m, sizeof(m), &n, &outlen); + + *hw_count = n.hw_count; + *sw_count = n.sw_count; + *lost_count = n.lost_count; +#else + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, m)); + *hw_count = m.hw_count; *sw_count = m.sw_count; *lost_count = m.lost_count; +#endif } /* lock */ @@ -80,7 +99,8 @@ uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint size_t outlen = sizeof(m); #endif checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_LOCK, m)); - return m.dma_reader_status; + // return m.dma_reader_status; + return 1; } void litepcie_release_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer) { diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 5ae3f34..2fa7ba1 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -160,6 +160,9 @@ void _check_ioctl(int status, const char *file, int line) { #if defined(_WIN32) fprintf(stderr, "Failed ioctl at %s:%d: %d\n", file, line, GetLastError()); + +#elif defined(__APPLE__) + fprintf(stderr, "Failed ioctl at %s:%d: %08x\n", file, line, status); #else fprintf(stderr, "Failed ioctl at %s:%d: %s\n", file, line, strerror(errno)); #endif diff --git a/src/samples.c b/src/samples.c index 72d34bc..8a86579 100644 --- a/src/samples.c +++ b/src/samples.c @@ -49,7 +49,7 @@ #endif -int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel) +int32_t samples_init(sampleStream_t* inst, file_t fd, uint8_t devIdx, uint8_t channel) { int32_t retVal = TS_STATUS_ERROR; char devName[TS_DMA_NAME_LEN] = {0}; @@ -60,15 +60,16 @@ int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel) inst->driver_buffer_count = 0; inst->active = 0; - snprintf(devName, TS_DMA_NAME_LEN, TS_DMA_NAME, TS_DMA_NAME_ARGS(channel, devIdx)); + // snprintf(devName, TS_DMA_NAME_LEN, TS_DMA_NAME, TS_DMA_NAME_ARGS(channel, devIdx)); - inst->dma.fds.fd = litepcie_open(devName, TS_DMA_OS_FLAGS); + // inst->dma.fds.fd = litepcie_open(devName, TS_DMA_OS_FLAGS); + inst->dma.fds.fd = fd; inst->dma.channel = channel; if((INVALID_HANDLE_VALUE != inst->dma.fds.fd) && litepcie_request_dma(&inst->dma, 0, 1)) { - litepcie_dma_set_loopback(&inst->dma, 0); + // litepcie_dma_set_loopback(&inst->dma, 0); retVal = TS_STATUS_OK; #if defined(__APPLE__) mach_vm_address_t writerAddress = 0; @@ -145,14 +146,16 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ { inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ); } - printf("Sample Get buffer %llu (%llu hw) (%d/%u)\r\n", inst->driver_buffer_count, inst->dma_buffer_count, retVal, bufferLen); if(buff_available > 0) { - memcpy(sampleBuffer, &inst->dma.buf_rd[(inst->driver_buffer_count % DMA_BUFFER_COUNT) * TS_SAMPLE_BUFFER_SIZE], TS_SAMPLE_BUFFER_SIZE); + memcpy(sampleBuffer, &(inst->dma.buf_rd[(inst->driver_buffer_count % DMA_BUFFER_COUNT) * TS_SAMPLE_BUFFER_SIZE]), TS_SAMPLE_BUFFER_SIZE); inst->driver_buffer_count++; retVal += TS_SAMPLE_BUFFER_SIZE; } - NS_DELAY(250); + else + { + NS_DELAY(250); + } } #else while (retVal < bufferLen) @@ -210,7 +213,9 @@ int32_t samples_teardown(sampleStream_t* inst) #endif litepcie_release_dma(&inst->dma, 0, 1); +#if !defined(__APPLE__) litepcie_close(inst->dma.fds.fd); +#endif } return retVal; diff --git a/src/samples.h b/src/samples.h index 18a6db7..09f79c3 100644 --- a/src/samples.h +++ b/src/samples.h @@ -42,7 +42,7 @@ typedef struct sampleStream_s * @param channel DMA Channel to read * @return int32_t TS_STATUS_OK if the DMA was initialized, else TS_STATUS_ERROR */ -int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel); +int32_t samples_init(sampleStream_t* inst, file_t fd, uint8_t devIdx, uint8_t channel); /** * @brief Enable or Disable the sample engine diff --git a/src/thunderscope.c b/src/thunderscope.c index c7f109d..0ee5f5a 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -98,7 +98,7 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx) return NULL; } - if(TS_STATUS_OK != samples_init(&pInst->samples, devIdx, 0)) + if(TS_STATUS_OK != samples_init(&pInst->samples, pInst->ctrl, devIdx, 0)) { LOG_ERROR("Failed to initialize samples"); ts_channel_destroy(pInst->pChannel); From 88d9f0fde17a83f30a7adc6e06f444fedc0c604a Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 19 Jan 2025 00:03:03 -0500 Subject: [PATCH 03/83] Cleanup DMA file handles --- litepcie/src/litepcie_dma.c | 4 ++-- src/samples.c | 12 +++++------- src/samples.h | 2 +- src/thunderscope.c | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index 0f33b22..269d372 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -73,13 +73,13 @@ void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t m.channel = dma->channel; m.sw_count = *sw_count; - IOConnectCallStructMethod(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, &m, sizeof(m), &n, &outlen); + IOConnectCallStructMethod(dma->fds.fd, LITEPCIE_IOCTL_DMA_READER, &m, sizeof(m), &n, &outlen); *hw_count = n.hw_count; *sw_count = n.sw_count; *lost_count = n.lost_count; #else - checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, m)); + checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_READER, m)); *hw_count = m.hw_count; *sw_count = m.sw_count; diff --git a/src/samples.c b/src/samples.c index 8a86579..7b814a9 100644 --- a/src/samples.c +++ b/src/samples.c @@ -49,7 +49,7 @@ #endif -int32_t samples_init(sampleStream_t* inst, file_t fd, uint8_t devIdx, uint8_t channel) +int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel) { int32_t retVal = TS_STATUS_ERROR; char devName[TS_DMA_NAME_LEN] = {0}; @@ -60,16 +60,15 @@ int32_t samples_init(sampleStream_t* inst, file_t fd, uint8_t devIdx, uint8_t ch inst->driver_buffer_count = 0; inst->active = 0; - // snprintf(devName, TS_DMA_NAME_LEN, TS_DMA_NAME, TS_DMA_NAME_ARGS(channel, devIdx)); + snprintf(devName, TS_DMA_NAME_LEN, TS_DMA_NAME, TS_DMA_NAME_ARGS(channel, devIdx)); - // inst->dma.fds.fd = litepcie_open(devName, TS_DMA_OS_FLAGS); - inst->dma.fds.fd = fd; + inst->dma.fds.fd = litepcie_open(devName, TS_DMA_OS_FLAGS); inst->dma.channel = channel; if((INVALID_HANDLE_VALUE != inst->dma.fds.fd) && litepcie_request_dma(&inst->dma, 0, 1)) { - // litepcie_dma_set_loopback(&inst->dma, 0); + litepcie_dma_set_loopback(&inst->dma, 0); retVal = TS_STATUS_OK; #if defined(__APPLE__) mach_vm_address_t writerAddress = 0; @@ -145,6 +144,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ if(buff_available > (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ)) { inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ); + inst->dropped_buffer_count++; } if(buff_available > 0) { @@ -213,9 +213,7 @@ int32_t samples_teardown(sampleStream_t* inst) #endif litepcie_release_dma(&inst->dma, 0, 1); -#if !defined(__APPLE__) litepcie_close(inst->dma.fds.fd); -#endif } return retVal; diff --git a/src/samples.h b/src/samples.h index 09f79c3..18a6db7 100644 --- a/src/samples.h +++ b/src/samples.h @@ -42,7 +42,7 @@ typedef struct sampleStream_s * @param channel DMA Channel to read * @return int32_t TS_STATUS_OK if the DMA was initialized, else TS_STATUS_ERROR */ -int32_t samples_init(sampleStream_t* inst, file_t fd, uint8_t devIdx, uint8_t channel); +int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel); /** * @brief Enable or Disable the sample engine diff --git a/src/thunderscope.c b/src/thunderscope.c index 0ee5f5a..c7f109d 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -98,7 +98,7 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx) return NULL; } - if(TS_STATUS_OK != samples_init(&pInst->samples, pInst->ctrl, devIdx, 0)) + if(TS_STATUS_OK != samples_init(&pInst->samples, devIdx, 0)) { LOG_ERROR("Failed to initialize samples"); ts_channel_destroy(pInst->pChannel); From 8c05bc37507df0cddbaabe7e0955a3cdb7ff69b2 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 19 Jan 2025 00:30:45 -0500 Subject: [PATCH 04/83] Cleanup DMA ioctl --- litepcie/src/litepcie_dma.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index 269d372..b9c3bf5 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -40,51 +40,35 @@ void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_e } void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { - struct litepcie_ioctl_dma_writer m, n; + struct litepcie_ioctl_dma_writer m; m.enable = enable; #if defined(__APPLE__) - size_t outlen = sizeof(n); + size_t outlen = sizeof(m); m.channel = dma->channel; m.sw_count = *sw_count; - - IOConnectCallStructMethod(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, &m, sizeof(m), &n, &outlen); +#endif - if(outlen != sizeof(n)) - { - printf(stderr, "IOCTL Output Len Invalid: %u, expected %u\r\n", outlen, sizeof(n)); - } - *hw_count = n.hw_count; - *sw_count = n.sw_count; - *lost_count = n.lost_count; -#else checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_WRITER, m)); *hw_count = m.hw_count; *sw_count = m.sw_count; *lost_count = m.lost_count; -#endif } void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { - struct litepcie_ioctl_dma_reader m, n; + struct litepcie_ioctl_dma_reader m; m.enable = enable; #if defined(__APPLE__) - size_t outlen = sizeof(n); + size_t outlen = sizeof(m); m.channel = dma->channel; m.sw_count = *sw_count; - - IOConnectCallStructMethod(dma->fds.fd, LITEPCIE_IOCTL_DMA_READER, &m, sizeof(m), &n, &outlen); +#endif - *hw_count = n.hw_count; - *sw_count = n.sw_count; - *lost_count = n.lost_count; -#else checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA_READER, m)); *hw_count = m.hw_count; *sw_count = m.sw_count; *lost_count = m.lost_count; -#endif } /* lock */ @@ -99,6 +83,7 @@ uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint size_t outlen = sizeof(m); #endif checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_LOCK, m)); + //TODO // return m.dma_reader_status; return 1; } From b079d05481da55cd8742f0af066c69024a8a96e9 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 30 Mar 2025 23:52:26 -0400 Subject: [PATCH 05/83] Fix link issues in build --- example/CMakeLists.txt | 9 +++++---- litepcie/CMakeLists.txt | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index c8c2308..f745855 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -4,10 +4,10 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/example") add_executable(thunderscope_test thunderscope_test.cpp) target_link_libraries(thunderscope_test tslitex_static) -target_link_libraries(thunderscope_test "-framework IOKit") if(WIN32) target_link_libraries(thunderscope_test setupapi) -elif(APPLE) +elseif(APPLE) + target_link_libraries(thunderscope_test "-framework IOKit") endif() @@ -15,9 +15,10 @@ endif() add_executable(thunderscope_fw thunderscope_fw.cpp) target_link_libraries(thunderscope_fw tslitex_static) -target_link_libraries(thunderscope_fw "-framework IOKit") if(WIN32) target_link_libraries(thunderscope_fw setupapi) -elif(APPLE) +elseif(APPLE) + target_link_libraries(thunderscope_fw "-framework IOKit") endif() + diff --git a/litepcie/CMakeLists.txt b/litepcie/CMakeLists.txt index b083a97..79cdb4e 100644 --- a/litepcie/CMakeLists.txt +++ b/litepcie/CMakeLists.txt @@ -49,6 +49,6 @@ target_include_directories(litepcie PUBLIC public_h include) if(WIN32) target_link_libraries(litepcie setupapi) -elif(APPLE) - target_link_libraries(tslitex "-framework IOKit") +elseif(APPLE) + target_link_libraries(litepcie "-framework IOKit") endif() \ No newline at end of file From 01811e095a1ca85ef419518714b53e0b54658e98 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 4 Apr 2025 01:11:45 -0400 Subject: [PATCH 06/83] Add reading samples from an ioctl on MacOS instead of polling. WIP. --- example/thunderscope_test.cpp | 17 ----------------- litepcie/public_h/litepcie_mac.h | 12 +++++++++++- src/samples.c | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 7080768..9eb1c9b 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -220,17 +220,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx); - // tsChannelHdl_t channels; - // ts_channel_init(&channels, fd); - // if(channels == NULL) - // { - // printf("Failed to create channels handle"); - // return; - // } - - // sampleStream_t samp; - // samples_init(&samp, 0, 0); - uint8_t* sampleBuffer = (uint8_t*)calloc(TS_SAMPLE_BUFFER_SIZE, 0x1000); uint64_t sampleLen = 0; @@ -271,8 +260,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { uint64_t data_sum = 0; //Start Sample capture - // samples_enable_set(&samp, 1); - // ts_channel_run(channels, 1); thunderscopeDataEnable(tsHdl, 1); auto startTime = std::chrono::steady_clock::now(); @@ -299,8 +286,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ auto endTime = std::chrono::steady_clock::now(); //Stop Samples - // samples_enable_set(&samp, 0); - // ts_channel_run(channels, 0); thunderscopeDataEnable(tsHdl, 0); auto deltaNs = std::chrono::duration_cast(endTime - startTime); @@ -318,8 +303,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ thunderscopeChannelConfigSet(tsHdl, i, &chConfig); } - // ts_channel_destroy(channels); - // samples_teardown(&samp); thunderscopeClose(tsHdl); if(sampleLen > 0) diff --git a/litepcie/public_h/litepcie_mac.h b/litepcie/public_h/litepcie_mac.h index d3875a9..8985072 100644 --- a/litepcie/public_h/litepcie_mac.h +++ b/litepcie/public_h/litepcie_mac.h @@ -69,7 +69,9 @@ enum LitePCIeMessageType { LITEPCIE_ICAP, LITEPCIE_FLASH, LITEPCIE_CONFIG_DMA, - LITEPCIE_CONFIG_DMA_LOCK + LITEPCIE_CONFIG_DMA_LOCK, + LITEPCIE_DMA_READ, + LITEPCIE_DMA_WRITE }; enum LitePCIeMemoryType { @@ -98,6 +100,12 @@ typedef struct litepcie_ioctl_icap { uint32_t data; } __attribute__((packed)) LitePCIeICAPCallData; +typedef struct litepcie_ioctl_dma_transfer_s { + uint32_t channel; + uint32_t length; + void* buffer_addr; +} __attribute__((packed)) litepcie_ioctl_dma_transfer_t; + #define LITEPCIE_IOCTL_FLASH LITEPCIE_FLASH #define LITEPCIE_IOCTL_ICAP LITEPCIE_ICAP @@ -109,5 +117,7 @@ typedef struct litepcie_ioctl_icap { #define LITEPCIE_IOCTL_LOCK LITEPCIE_CONFIG_DMA_LOCK // #define LITEPCIE_IOCTL_MMAP_DMA_WRITER_UPDATE //TBD // #define LITEPCIE_IOCTL_MMAP_DMA_READER_UPDATE //TBD +#define LITEPCIE_IOCTL_DMA_READ LITEPCIE_DMA_READ +#define LITEPCIE_IOCTL_DMA_WRITE LITEPCIE_DMA_WRITE #endif /* _LINUX_LITEPCIE_H */ diff --git a/src/samples.c b/src/samples.c index 7b814a9..88e1d25 100644 --- a/src/samples.c +++ b/src/samples.c @@ -136,6 +136,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ #elif defined(__APPLE__) while(retVal < (int32_t)bufferLen) { + #if 0 //POLL litepcie_dma_writer(&inst->dma, inst->active, &inst->dma_buffer_count, &inst->driver_buffer_count, @@ -156,6 +157,20 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ { NS_DELAY(250); } + #else + size_t readLen = bufferLen - retVal; + litepcie_ioctl_dma_transfer_t sampleTransfer = {.channel=0, .length=readLen, .buffer_addr=&sampleBuffer[retVal]}; + size_t outlen = sizeof(litepcie_ioctl_dma_transfer_t); + checked_ioctl(ioctl_args(inst->dma.fds.fd, LITEPCIE_IOCTL_DMA_READ, sampleTransfer)); + if(sampleTransfer.length == 0) + { + LOG_ERROR("Failed to acquire sample buffer"); + retVal = TS_STATUS_ERROR; + break; + } + //Actual Read Length gets returned in the transfer struct + retVal += sampleTransfer.length; + #endif } #else while (retVal < bufferLen) From 6467e5bb9a0f811ccf4941bcbc9905e9020df701 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Mon, 28 Apr 2025 00:40:24 -0400 Subject: [PATCH 07/83] Improve compatibility between beta and dev/prod designs --- bindings/python/tslitex.pxd | 5 +- bindings/python/tslitex.pyx | 4 +- example/thunderscope_fw.cpp | 18 +- example/thunderscope_test.cpp | 30 ++-- include/thunderscope.h | 4 +- include/ts_common.h | 7 + litepcie/public_h/csr.h | 308 +++++++++++++++++++++------------- src/adc.c | 16 +- src/gpio.c | 6 + src/gpio.h | 1 + src/i2c.c | 76 ++++++--- src/i2c.h | 1 + src/platform.c | 13 ++ src/platform.h | 29 +++- src/thunderscope.c | 139 +++++++++++---- src/ts_channel.c | 54 +++++- 16 files changed, 489 insertions(+), 222 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 279e2a7..7ea6dae 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -34,6 +34,7 @@ cdef extern from "thunderscope.h": cdef struct tsDeviceInfo_s: uint32_t device_id + uint32_t hw_id char device_path[256] char identity[256] char serial_number[256] @@ -56,6 +57,8 @@ cdef extern from "thunderscope.h": uint32_t vcc_int uint32_t vcc_aux uint32_t vcc_bram + uint8_t frontend_power_good + uint8_t acq_power_good ctypedef sysHealth_s sysHealth_t @@ -75,7 +78,7 @@ cdef extern from "thunderscope.h": int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t* info) - tsHandle_t thunderscopeOpen(uint32_t devIdx) + tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) int32_t thunderscopeClose(tsHandle_t ts) diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index 6721258..dc3cec8 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -166,11 +166,11 @@ cdef class Thunderscope: def __cinit__(self, dev_idx: int): self.channel = [] - def __init__(self, dev_idx: int): + def __init__(self, dev_idx: int, skip_init:bool = False): self._sample_rate = 1000000000 self._sample_mode = 256 self._enable = 0 - self._tsHandle = tslitex.thunderscopeOpen(dev_idx) + self._tsHandle = tslitex.thunderscopeOpen(dev_idx, skip_init) if self._tsHandle == NULL: raise ValueError(f"Failed to Open Thunderscope Device {dev_idx}", dev_idx) for ch in range(4): diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 4e15493..102c260 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -139,23 +139,23 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } - ts = thunderscopeOpen(idx); + ts = thunderscopeOpen(idx, true); if(ts == NULL) { fprintf(stderr, "Could not init driver\n"); exit(EXIT_FAILURE); } - printf("\x1b[1m[> FPGA/SoC Information:\x1b[0m\n"); + printf("\x1b[1m[> Device Information:\x1b[0m\n"); printf("------------------------\n"); printf("FPGA Identifier: %s.\n", infos.identity); - - result = thunderscopeStatusGet(ts, &status); - if(result == TS_STATUS_OK) + if(infos.hw_id & TS_HW_ID_VALID_MASK) + { + printf("HW Rev %02d - %s\n", infos.hw_id & TS_HW_ID_REV_MASK, + (infos.hw_id & TS_HW_ID_VARIANT_MASK) ? "PCIe" : "TB" ); + } + else { - printf("FPGA Temperature: %0.1f \u00B0C\n", (float_t)status.sys_health.temp_c / 1000); - printf("FPGA VCC-INT: %0.2f V\n", (float_t)status.sys_health.vcc_int / 1000); - printf("FPGA VCC-AUX: %0.2f V\n", (float_t)status.sys_health.vcc_aux / 1000); - printf("FPGA VCC-BRAM: %0.2f V\n", (float_t)status.sys_health.vcc_bram / 1000); + printf("HW Rev Beta\n"); } if(0 == strcmp(arg, "factory_restore")) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index f4377f5..980549a 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -120,10 +120,10 @@ void configure_pll_en(file_t fd, uint32_t enable) { } void control_led(file_t fd, uint32_t enable) { - uint32_t control_value = litepcie_readl(fd, CSR_LEDS_OUT_ADDR); + uint32_t control_value = litepcie_readl(fd, CSR_DEV_STATUS_LEDS_ADDR); control_value &= ~(1 * AFE_STATUS_LDO_PWR_GOOD); control_value |= (enable * AFE_STATUS_LDO_PWR_GOOD); - litepcie_writel(fd, CSR_LEDS_OUT_ADDR, control_value); + litepcie_writel(fd, CSR_DEV_STATUS_LEDS_ADDR, control_value); } static uint32_t read_flash_word(file_t fd, uint32_t flash_addr) @@ -159,7 +159,7 @@ static void test_io(file_t fd) /* Write to scratch register. */ printf("Write 0x12345678 to Scratch register:\n"); - litepcie_writel(fd, CSR_CTRL_SCRATCH_ADDR, 0x0); + litepcie_writel(fd, CSR_CTRL_SCRATCH_ADDR, 0x12345678); printf("Read: 0x%08x\n", litepcie_readl(fd, CSR_CTRL_SCRATCH_ADDR)); /* Read from scratch register. */ @@ -183,6 +183,7 @@ static void test_io(file_t fd) i2c_t i2cDev; i2cDev.fd = fd; + i2cDev.peripheral_baseaddr = CSR_I2CBUS_BASE; i2c_rate_set(i2cDev, I2C_400KHz); for (unsigned char addr = 0; addr < 0x80; addr++) { @@ -200,11 +201,11 @@ static void test_io(file_t fd) } spi_bus_t spimaster; - spi_bus_init(&spimaster, fd, CSR_MAIN_SPI_BASE, CSR_MAIN_SPI_CS_SEL_SIZE); + spi_bus_init(&spimaster, fd, CSR_SPIBUS_BASE, CSR_SPIBUS_SPI0_CS_SEL_SIZE); uint8_t data[2] = {0x01, 0x02}; - for (int i = 0; i < CSR_MAIN_SPI_CS_SEL_SIZE; i++) { + for (int i = 0; i < CSR_SPIBUS_SPI0_CS_SEL_SIZE; i++) { spi_dev_t spiDev; spi_dev_init(&spiDev, &spimaster, i); for (int reg = 0; reg < 10; reg++) { @@ -218,7 +219,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ uint32_t volt_scale_mV, int32_t offset_mV, uint8_t ac_couple, uint8_t term) { uint8_t numChan = 0; - tsHandle_t tsHdl = thunderscopeOpen(idx); + tsHandle_t tsHdl = thunderscopeOpen(idx, false); // tsChannelHdl_t channels; // ts_channel_init(&channels, fd); @@ -260,9 +261,9 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ // ts_channel_set_adc_test(channels, HMCAD15_TEST_SYNC, 0, 0); printf("- Checking HMCAD1520 Sample Rate..."); - litepcie_writel(fd, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_STAT_RST_OFFSET); + litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); NS_DELAY(500000000); - uint32_t rate = litepcie_readl(fd, CSR_ADC_HAD1511_SAMPLE_COUNT_ADDR) * 2; + uint32_t rate = litepcie_readl(fd, CSR_ADC_HMCAD1520_SAMPLE_COUNT_ADDR) * 2; printf(" %d Samples/S\r\n", rate); @@ -620,7 +621,7 @@ int main(int argc, char** argv) } - printf("\x1b[1m[> FPGA/SoC Information:\x1b[0m\n"); + printf("\x1b[1m[> Device Information:\x1b[0m\n"); printf("------------------------\n"); for (i = 0; i < 256; i++) @@ -628,7 +629,16 @@ int main(int argc, char** argv) fpga_identifier[i] = litepcie_readl(fd, CSR_IDENTIFIER_MEM_BASE + 4 * i); } printf("FPGA Identifier: %s.\n", fpga_identifier); - + uint32_t hw_info = litepcie_readl(fd, CSR_DEV_STATUS_HW_ID_ADDR); + if(hw_info & TS_HW_ID_VALID_MASK) + { + printf("HW Rev %02d - %s\n", hw_info & TS_HW_ID_REV_MASK, + (hw_info & TS_HW_ID_VARIANT_MASK) ? "PCIe" : "TB" ); + } + else + { + printf("HW Rev Beta\n"); + } #ifdef CSR_DNA_BASE printf("FPGA DNA: 0x%08x%08x\n", litepcie_readl(fd, CSR_DNA_ID_ADDR + 4 * 0), diff --git a/include/thunderscope.h b/include/thunderscope.h index 56b1986..80d129c 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -33,9 +33,11 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info); * @brief Open a new Thunderscope device instance * * @param devIdx Device index to open + * @param skip_init Do not initialize device peripherals. Useful when doing gateware upgrades + * or other maintenance. * @return tsHandle_t Handle to the Thunderscope device */ -tsHandle_t thunderscopeOpen(uint32_t devIdx); +tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init); /** * @brief Close the Thunderscope device diff --git a/include/ts_common.h b/include/ts_common.h index d51c4ea..92a92fc 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -30,6 +30,10 @@ extern "C" { #define TS_MAX_QUAD_CH_RATE (250000000) #define TS_MIN_SAMPLE_RATE (15000000) +#define TS_HW_ID_REV_MASK (7) +#define TS_HW_ID_VARIANT_MASK (1 << 8) +#define TS_HW_ID_VALID_MASK (1 << 9) + /** * @brief Opaque Handle to a Thunderscope device instance * @@ -52,6 +56,7 @@ typedef enum tsChannelTerm_e typedef struct tsDeviceInfo_s { uint32_t device_id; + uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ char device_path[TS_IDENT_STR_LEN]; char identity[TS_IDENT_STR_LEN]; char serial_number[TS_IDENT_STR_LEN]; @@ -73,6 +78,8 @@ typedef struct sysHealth_s { uint32_t vcc_int; uint32_t vcc_aux; uint32_t vcc_bram; + uint8_t frontend_power_good; + uint8_t acq_power_good; } sysHealth_t; typedef struct tsScopeState_s diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index c5facdb..d7ee6ab 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (f63d4a833) on 2024-12-26 19:07:59 +// Auto-generated by LiteX (c213c4274) on 2025-04-27 23:07:46 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -267,26 +267,45 @@ /* XADC Fields */ +/* DEV_STATUS Registers */ +#define CSR_DEV_STATUS_BASE (CSR_BASE + 0x6000L) +#define CSR_DEV_STATUS_LEDS_ADDR (CSR_BASE + 0x6000L) +#define CSR_DEV_STATUS_LEDS_SIZE 1 +#define CSR_DEV_STATUS_HW_ID_ADDR (CSR_BASE + 0x6004L) +#define CSR_DEV_STATUS_HW_ID_SIZE 1 + +/* DEV_STATUS Fields */ +#define CSR_DEV_STATUS_HW_ID_HW_REV_OFFSET 0 +#define CSR_DEV_STATUS_HW_ID_HW_REV_SIZE 3 +#define CSR_DEV_STATUS_HW_ID_HW_VARIANT_OFFSET 8 +#define CSR_DEV_STATUS_HW_ID_HW_VARIANT_SIZE 1 +#define CSR_DEV_STATUS_HW_ID_HW_VALID_OFFSET 9 +#define CSR_DEV_STATUS_HW_ID_HW_VALID_SIZE 1 +#define CSR_DEV_STATUS_HW_ID_NUM_LEDS_OFFSET 16 +#define CSR_DEV_STATUS_HW_ID_NUM_LEDS_SIZE 4 + /* ADC Registers */ -#define CSR_ADC_BASE (CSR_BASE + 0x6000L) -#define CSR_ADC_CONTROL_ADDR (CSR_BASE + 0x6000L) +#define CSR_ADC_BASE (CSR_BASE + 0x6800L) +#define CSR_ADC_CONTROL_ADDR (CSR_BASE + 0x6800L) #define CSR_ADC_CONTROL_SIZE 1 -#define CSR_ADC_TRIGGER_CONTROL_ADDR (CSR_BASE + 0x6004L) +#define CSR_ADC_STATUS_ADDR (CSR_BASE + 0x6804L) +#define CSR_ADC_STATUS_SIZE 1 +#define CSR_ADC_TRIGGER_CONTROL_ADDR (CSR_BASE + 0x6808L) #define CSR_ADC_TRIGGER_CONTROL_SIZE 1 -#define CSR_ADC_HAD1511_CONTROL_ADDR (CSR_BASE + 0x6008L) -#define CSR_ADC_HAD1511_CONTROL_SIZE 1 -#define CSR_ADC_HAD1511_STATUS_ADDR (CSR_BASE + 0x600cL) -#define CSR_ADC_HAD1511_STATUS_SIZE 1 -#define CSR_ADC_HAD1511_DOWNSAMPLING_ADDR (CSR_BASE + 0x6010L) -#define CSR_ADC_HAD1511_DOWNSAMPLING_SIZE 1 -#define CSR_ADC_HAD1511_RANGE_ADDR (CSR_BASE + 0x6014L) -#define CSR_ADC_HAD1511_RANGE_SIZE 1 -#define CSR_ADC_HAD1511_BITSLIP_COUNT_ADDR (CSR_BASE + 0x6018L) -#define CSR_ADC_HAD1511_BITSLIP_COUNT_SIZE 1 -#define CSR_ADC_HAD1511_SAMPLE_COUNT_ADDR (CSR_BASE + 0x601cL) -#define CSR_ADC_HAD1511_SAMPLE_COUNT_SIZE 1 -#define CSR_ADC_HAD1511_DATA_CHANNELS_ADDR (CSR_BASE + 0x6020L) -#define CSR_ADC_HAD1511_DATA_CHANNELS_SIZE 1 +#define CSR_ADC_HMCAD1520_CONTROL_ADDR (CSR_BASE + 0x680cL) +#define CSR_ADC_HMCAD1520_CONTROL_SIZE 1 +#define CSR_ADC_HMCAD1520_STATUS_ADDR (CSR_BASE + 0x6810L) +#define CSR_ADC_HMCAD1520_STATUS_SIZE 1 +#define CSR_ADC_HMCAD1520_DOWNSAMPLING_ADDR (CSR_BASE + 0x6814L) +#define CSR_ADC_HMCAD1520_DOWNSAMPLING_SIZE 1 +#define CSR_ADC_HMCAD1520_RANGE_ADDR (CSR_BASE + 0x6818L) +#define CSR_ADC_HMCAD1520_RANGE_SIZE 1 +#define CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR (CSR_BASE + 0x681cL) +#define CSR_ADC_HMCAD1520_BITSLIP_COUNT_SIZE 1 +#define CSR_ADC_HMCAD1520_SAMPLE_COUNT_ADDR (CSR_BASE + 0x6820L) +#define CSR_ADC_HMCAD1520_SAMPLE_COUNT_SIZE 1 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR (CSR_BASE + 0x6824L) +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_SIZE 1 /* ADC Fields */ #define CSR_ADC_CONTROL_ACQ_EN_OFFSET 0 @@ -297,33 +316,37 @@ #define CSR_ADC_CONTROL_RST_SIZE 1 #define CSR_ADC_CONTROL_PWR_DOWN_OFFSET 3 #define CSR_ADC_CONTROL_PWR_DOWN_SIZE 1 +#define CSR_ADC_STATUS_ACQ_PG_OFFSET 0 +#define CSR_ADC_STATUS_ACQ_PG_SIZE 1 #define CSR_ADC_TRIGGER_CONTROL_ENABLE_OFFSET 0 #define CSR_ADC_TRIGGER_CONTROL_ENABLE_SIZE 1 -#define CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET 0 -#define CSR_ADC_HAD1511_CONTROL_FRAME_RST_SIZE 1 -#define CSR_ADC_HAD1511_CONTROL_DELAY_RST_OFFSET 1 -#define CSR_ADC_HAD1511_CONTROL_DELAY_RST_SIZE 1 -#define CSR_ADC_HAD1511_CONTROL_DELAY_INC_OFFSET 2 -#define CSR_ADC_HAD1511_CONTROL_DELAY_INC_SIZE 1 -#define CSR_ADC_HAD1511_CONTROL_STAT_RST_OFFSET 3 -#define CSR_ADC_HAD1511_CONTROL_STAT_RST_SIZE 1 -#define CSR_ADC_HAD1511_RANGE_MIN01_OFFSET 0 -#define CSR_ADC_HAD1511_RANGE_MIN01_SIZE 8 -#define CSR_ADC_HAD1511_RANGE_MAX01_OFFSET 8 -#define CSR_ADC_HAD1511_RANGE_MAX01_SIZE 8 -#define CSR_ADC_HAD1511_RANGE_MIN23_OFFSET 16 -#define CSR_ADC_HAD1511_RANGE_MIN23_SIZE 8 -#define CSR_ADC_HAD1511_RANGE_MAX23_OFFSET 24 -#define CSR_ADC_HAD1511_RANGE_MAX23_SIZE 8 -#define CSR_ADC_HAD1511_DATA_CHANNELS_SHUFFLE_OFFSET 0 -#define CSR_ADC_HAD1511_DATA_CHANNELS_SHUFFLE_SIZE 2 -#define CSR_ADC_HAD1511_DATA_CHANNELS_RUN_LENGTH_OFFSET 2 -#define CSR_ADC_HAD1511_DATA_CHANNELS_RUN_LENGTH_SIZE 6 +#define CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET 0 +#define CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_SIZE 1 +#define CSR_ADC_HMCAD1520_CONTROL_DELAY_RST_OFFSET 1 +#define CSR_ADC_HMCAD1520_CONTROL_DELAY_RST_SIZE 1 +#define CSR_ADC_HMCAD1520_CONTROL_DELAY_INC_OFFSET 2 +#define CSR_ADC_HMCAD1520_CONTROL_DELAY_INC_SIZE 1 +#define CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET 3 +#define CSR_ADC_HMCAD1520_CONTROL_STAT_RST_SIZE 1 +#define CSR_ADC_HMCAD1520_RANGE_MIN01_OFFSET 0 +#define CSR_ADC_HMCAD1520_RANGE_MIN01_SIZE 8 +#define CSR_ADC_HMCAD1520_RANGE_MAX01_OFFSET 8 +#define CSR_ADC_HMCAD1520_RANGE_MAX01_SIZE 8 +#define CSR_ADC_HMCAD1520_RANGE_MIN23_OFFSET 16 +#define CSR_ADC_HMCAD1520_RANGE_MIN23_SIZE 8 +#define CSR_ADC_HMCAD1520_RANGE_MAX23_OFFSET 24 +#define CSR_ADC_HMCAD1520_RANGE_MAX23_SIZE 8 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET 0 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_SIZE 2 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_RUN_LENGTH_OFFSET 2 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_RUN_LENGTH_SIZE 6 /* FRONTEND Registers */ -#define CSR_FRONTEND_BASE (CSR_BASE + 0x6800L) -#define CSR_FRONTEND_CONTROL_ADDR (CSR_BASE + 0x6800L) +#define CSR_FRONTEND_BASE (CSR_BASE + 0x7000L) +#define CSR_FRONTEND_CONTROL_ADDR (CSR_BASE + 0x7000L) #define CSR_FRONTEND_CONTROL_SIZE 1 +#define CSR_FRONTEND_STATUS_ADDR (CSR_BASE + 0x7004L) +#define CSR_FRONTEND_STATUS_SIZE 1 /* FRONTEND Fields */ #define CSR_FRONTEND_CONTROL_FE_EN_OFFSET 0 @@ -334,93 +357,136 @@ #define CSR_FRONTEND_CONTROL_ATTENUATION_SIZE 4 #define CSR_FRONTEND_CONTROL_TERMINATION_OFFSET 24 #define CSR_FRONTEND_CONTROL_TERMINATION_SIZE 4 - -/* I2C Registers */ -#define CSR_I2C_BASE (CSR_BASE + 0x7000L) -#define CSR_I2C_PHY_SPEED_MODE_ADDR (CSR_BASE + 0x7000L) -#define CSR_I2C_PHY_SPEED_MODE_SIZE 1 -#define CSR_I2C_MASTER_ACTIVE_ADDR (CSR_BASE + 0x7004L) -#define CSR_I2C_MASTER_ACTIVE_SIZE 1 -#define CSR_I2C_MASTER_SETTINGS_ADDR (CSR_BASE + 0x7008L) -#define CSR_I2C_MASTER_SETTINGS_SIZE 1 -#define CSR_I2C_MASTER_ADDR_ADDR (CSR_BASE + 0x700cL) -#define CSR_I2C_MASTER_ADDR_SIZE 1 -#define CSR_I2C_MASTER_RXTX_ADDR (CSR_BASE + 0x7010L) -#define CSR_I2C_MASTER_RXTX_SIZE 1 -#define CSR_I2C_MASTER_STATUS_ADDR (CSR_BASE + 0x7014L) -#define CSR_I2C_MASTER_STATUS_SIZE 1 - -/* I2C Fields */ -#define CSR_I2C_MASTER_SETTINGS_LEN_TX_OFFSET 0 -#define CSR_I2C_MASTER_SETTINGS_LEN_TX_SIZE 3 -#define CSR_I2C_MASTER_SETTINGS_LEN_RX_OFFSET 8 -#define CSR_I2C_MASTER_SETTINGS_LEN_RX_SIZE 3 -#define CSR_I2C_MASTER_SETTINGS_RECOVER_OFFSET 16 -#define CSR_I2C_MASTER_SETTINGS_RECOVER_SIZE 1 -#define CSR_I2C_MASTER_STATUS_TX_READY_OFFSET 0 -#define CSR_I2C_MASTER_STATUS_TX_READY_SIZE 1 -#define CSR_I2C_MASTER_STATUS_RX_READY_OFFSET 1 -#define CSR_I2C_MASTER_STATUS_RX_READY_SIZE 1 -#define CSR_I2C_MASTER_STATUS_NACK_OFFSET 8 -#define CSR_I2C_MASTER_STATUS_NACK_SIZE 1 -#define CSR_I2C_MASTER_STATUS_TX_UNFINISHED_OFFSET 16 -#define CSR_I2C_MASTER_STATUS_TX_UNFINISHED_SIZE 1 -#define CSR_I2C_MASTER_STATUS_RX_UNFINISHED_OFFSET 17 -#define CSR_I2C_MASTER_STATUS_RX_UNFINISHED_SIZE 1 - -/* LEDS Registers */ -#define CSR_LEDS_BASE (CSR_BASE + 0x7800L) -#define CSR_LEDS_OUT_ADDR (CSR_BASE + 0x7800L) -#define CSR_LEDS_OUT_SIZE 1 -#define CSR_LEDS_PWM_ENABLE_ADDR (CSR_BASE + 0x7804L) -#define CSR_LEDS_PWM_ENABLE_SIZE 1 -#define CSR_LEDS_PWM_WIDTH_ADDR (CSR_BASE + 0x7808L) -#define CSR_LEDS_PWM_WIDTH_SIZE 1 -#define CSR_LEDS_PWM_PERIOD_ADDR (CSR_BASE + 0x780cL) -#define CSR_LEDS_PWM_PERIOD_SIZE 1 - -/* LEDS Fields */ - -/* MAIN_SPI Registers */ -#define CSR_MAIN_SPI_BASE (CSR_BASE + 0x8000L) -#define CSR_MAIN_SPI_CONTROL_ADDR (CSR_BASE + 0x8000L) -#define CSR_MAIN_SPI_CONTROL_SIZE 1 -#define CSR_MAIN_SPI_STATUS_ADDR (CSR_BASE + 0x8004L) -#define CSR_MAIN_SPI_STATUS_SIZE 1 -#define CSR_MAIN_SPI_MOSI_ADDR (CSR_BASE + 0x8008L) -#define CSR_MAIN_SPI_MOSI_SIZE 1 -#define CSR_MAIN_SPI_MISO_ADDR (CSR_BASE + 0x800cL) -#define CSR_MAIN_SPI_MISO_SIZE 1 -#define CSR_MAIN_SPI_CS_ADDR (CSR_BASE + 0x8010L) -#define CSR_MAIN_SPI_CS_SIZE 1 -#define CSR_MAIN_SPI_LOOPBACK_ADDR (CSR_BASE + 0x8014L) -#define CSR_MAIN_SPI_LOOPBACK_SIZE 1 - -/* MAIN_SPI Fields */ -#define CSR_MAIN_SPI_CONTROL_START_OFFSET 0 -#define CSR_MAIN_SPI_CONTROL_START_SIZE 1 -#define CSR_MAIN_SPI_CONTROL_LENGTH_OFFSET 8 -#define CSR_MAIN_SPI_CONTROL_LENGTH_SIZE 8 -#define CSR_MAIN_SPI_STATUS_DONE_OFFSET 0 -#define CSR_MAIN_SPI_STATUS_DONE_SIZE 1 -#define CSR_MAIN_SPI_STATUS_MODE_OFFSET 1 -#define CSR_MAIN_SPI_STATUS_MODE_SIZE 1 -#define CSR_MAIN_SPI_CS_SEL_OFFSET 0 -#define CSR_MAIN_SPI_CS_SEL_SIZE 5 -#define CSR_MAIN_SPI_CS_MODE_OFFSET 16 -#define CSR_MAIN_SPI_CS_MODE_SIZE 1 -#define CSR_MAIN_SPI_LOOPBACK_MODE_OFFSET 0 -#define CSR_MAIN_SPI_LOOPBACK_MODE_SIZE 1 +#define CSR_FRONTEND_STATUS_FE_PG_OFFSET 0 +#define CSR_FRONTEND_STATUS_FE_PG_SIZE 1 /* PROBE_COMPENSATION Registers */ -#define CSR_PROBE_COMPENSATION_BASE (CSR_BASE + 0x8800L) -#define CSR_PROBE_COMPENSATION_ENABLE_ADDR (CSR_BASE + 0x8800L) +#define CSR_PROBE_COMPENSATION_BASE (CSR_BASE + 0x7800L) +#define CSR_PROBE_COMPENSATION_ENABLE_ADDR (CSR_BASE + 0x7800L) #define CSR_PROBE_COMPENSATION_ENABLE_SIZE 1 -#define CSR_PROBE_COMPENSATION_WIDTH_ADDR (CSR_BASE + 0x8804L) +#define CSR_PROBE_COMPENSATION_WIDTH_ADDR (CSR_BASE + 0x7804L) #define CSR_PROBE_COMPENSATION_WIDTH_SIZE 1 -#define CSR_PROBE_COMPENSATION_PERIOD_ADDR (CSR_BASE + 0x8808L) +#define CSR_PROBE_COMPENSATION_PERIOD_ADDR (CSR_BASE + 0x7808L) #define CSR_PROBE_COMPENSATION_PERIOD_SIZE 1 /* PROBE_COMPENSATION Fields */ +/* I2CBUS Registers */ +#define CSR_I2CBUS_BASE (CSR_BASE + 0x8000L) +#define CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR (CSR_BASE + 0x8000L) +#define CSR_I2CBUS_I2C0_PHY_SPEED_MODE_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_ACTIVE_ADDR (CSR_BASE + 0x8004L) +#define CSR_I2CBUS_I2C0_MASTER_ACTIVE_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_ADDR (CSR_BASE + 0x8008L) +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_ADDR_ADDR (CSR_BASE + 0x800cL) +#define CSR_I2CBUS_I2C0_MASTER_ADDR_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_RXTX_ADDR (CSR_BASE + 0x8010L) +#define CSR_I2CBUS_I2C0_MASTER_RXTX_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_ADDR (CSR_BASE + 0x8014L) +#define CSR_I2CBUS_I2C0_MASTER_STATUS_SIZE 1 +#define CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR (CSR_BASE + 0x8018L) +#define CSR_I2CBUS_I2C1_PHY_SPEED_MODE_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_ACTIVE_ADDR (CSR_BASE + 0x801cL) +#define CSR_I2CBUS_I2C1_MASTER_ACTIVE_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_ADDR (CSR_BASE + 0x8020L) +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_ADDR_ADDR (CSR_BASE + 0x8024L) +#define CSR_I2CBUS_I2C1_MASTER_ADDR_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_RXTX_ADDR (CSR_BASE + 0x8028L) +#define CSR_I2CBUS_I2C1_MASTER_RXTX_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_ADDR (CSR_BASE + 0x802cL) +#define CSR_I2CBUS_I2C1_MASTER_STATUS_SIZE 1 + +/* I2CBUS Fields */ +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_LEN_TX_OFFSET 0 +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_LEN_TX_SIZE 3 +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_LEN_RX_OFFSET 8 +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_LEN_RX_SIZE 3 +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_RECOVER_OFFSET 16 +#define CSR_I2CBUS_I2C0_MASTER_SETTINGS_RECOVER_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_TX_READY_OFFSET 0 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_TX_READY_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_RX_READY_OFFSET 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_RX_READY_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_NACK_OFFSET 8 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_NACK_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_TX_UNFINISHED_OFFSET 16 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_TX_UNFINISHED_SIZE 1 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_RX_UNFINISHED_OFFSET 17 +#define CSR_I2CBUS_I2C0_MASTER_STATUS_RX_UNFINISHED_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_LEN_TX_OFFSET 0 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_LEN_TX_SIZE 3 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_LEN_RX_OFFSET 8 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_LEN_RX_SIZE 3 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_RECOVER_OFFSET 16 +#define CSR_I2CBUS_I2C1_MASTER_SETTINGS_RECOVER_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_TX_READY_OFFSET 0 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_TX_READY_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_RX_READY_OFFSET 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_RX_READY_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_NACK_OFFSET 8 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_NACK_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_TX_UNFINISHED_OFFSET 16 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_TX_UNFINISHED_SIZE 1 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_RX_UNFINISHED_OFFSET 17 +#define CSR_I2CBUS_I2C1_MASTER_STATUS_RX_UNFINISHED_SIZE 1 + +/* SPIBUS Registers */ +#define CSR_SPIBUS_BASE (CSR_BASE + 0x8800L) +#define CSR_SPIBUS_SPI0_CONTROL_ADDR (CSR_BASE + 0x8800L) +#define CSR_SPIBUS_SPI0_CONTROL_SIZE 1 +#define CSR_SPIBUS_SPI0_STATUS_ADDR (CSR_BASE + 0x8804L) +#define CSR_SPIBUS_SPI0_STATUS_SIZE 1 +#define CSR_SPIBUS_SPI0_MOSI_ADDR (CSR_BASE + 0x8808L) +#define CSR_SPIBUS_SPI0_MOSI_SIZE 1 +#define CSR_SPIBUS_SPI0_MISO_ADDR (CSR_BASE + 0x880cL) +#define CSR_SPIBUS_SPI0_MISO_SIZE 1 +#define CSR_SPIBUS_SPI0_CS_ADDR (CSR_BASE + 0x8810L) +#define CSR_SPIBUS_SPI0_CS_SIZE 1 +#define CSR_SPIBUS_SPI0_LOOPBACK_ADDR (CSR_BASE + 0x8814L) +#define CSR_SPIBUS_SPI0_LOOPBACK_SIZE 1 +#define CSR_SPIBUS_SPI1_CONTROL_ADDR (CSR_BASE + 0x8818L) +#define CSR_SPIBUS_SPI1_CONTROL_SIZE 1 +#define CSR_SPIBUS_SPI1_STATUS_ADDR (CSR_BASE + 0x881cL) +#define CSR_SPIBUS_SPI1_STATUS_SIZE 1 +#define CSR_SPIBUS_SPI1_MOSI_ADDR (CSR_BASE + 0x8820L) +#define CSR_SPIBUS_SPI1_MOSI_SIZE 1 +#define CSR_SPIBUS_SPI1_MISO_ADDR (CSR_BASE + 0x8824L) +#define CSR_SPIBUS_SPI1_MISO_SIZE 1 +#define CSR_SPIBUS_SPI1_CS_ADDR (CSR_BASE + 0x8828L) +#define CSR_SPIBUS_SPI1_CS_SIZE 1 +#define CSR_SPIBUS_SPI1_LOOPBACK_ADDR (CSR_BASE + 0x882cL) +#define CSR_SPIBUS_SPI1_LOOPBACK_SIZE 1 + +/* SPIBUS Fields */ +#define CSR_SPIBUS_SPI0_CONTROL_START_OFFSET 0 +#define CSR_SPIBUS_SPI0_CONTROL_START_SIZE 1 +#define CSR_SPIBUS_SPI0_CONTROL_LENGTH_OFFSET 8 +#define CSR_SPIBUS_SPI0_CONTROL_LENGTH_SIZE 8 +#define CSR_SPIBUS_SPI0_STATUS_DONE_OFFSET 0 +#define CSR_SPIBUS_SPI0_STATUS_DONE_SIZE 1 +#define CSR_SPIBUS_SPI0_STATUS_MODE_OFFSET 1 +#define CSR_SPIBUS_SPI0_STATUS_MODE_SIZE 1 +#define CSR_SPIBUS_SPI0_CS_SEL_OFFSET 0 +#define CSR_SPIBUS_SPI0_CS_SEL_SIZE 4 +#define CSR_SPIBUS_SPI0_CS_MODE_OFFSET 16 +#define CSR_SPIBUS_SPI0_CS_MODE_SIZE 1 +#define CSR_SPIBUS_SPI0_LOOPBACK_MODE_OFFSET 0 +#define CSR_SPIBUS_SPI0_LOOPBACK_MODE_SIZE 1 +#define CSR_SPIBUS_SPI1_CONTROL_START_OFFSET 0 +#define CSR_SPIBUS_SPI1_CONTROL_START_SIZE 1 +#define CSR_SPIBUS_SPI1_CONTROL_LENGTH_OFFSET 8 +#define CSR_SPIBUS_SPI1_CONTROL_LENGTH_SIZE 8 +#define CSR_SPIBUS_SPI1_STATUS_DONE_OFFSET 0 +#define CSR_SPIBUS_SPI1_STATUS_DONE_SIZE 1 +#define CSR_SPIBUS_SPI1_STATUS_MODE_OFFSET 1 +#define CSR_SPIBUS_SPI1_STATUS_MODE_SIZE 1 +#define CSR_SPIBUS_SPI1_CS_SEL_OFFSET 0 +#define CSR_SPIBUS_SPI1_CS_SEL_SIZE 1 +#define CSR_SPIBUS_SPI1_CS_MODE_OFFSET 16 +#define CSR_SPIBUS_SPI1_CS_MODE_SIZE 1 +#define CSR_SPIBUS_SPI1_LOOPBACK_MODE_OFFSET 0 +#define CSR_SPIBUS_SPI1_LOOPBACK_MODE_SIZE 1 + #endif /* ! __GENERATED_CSR_H */ diff --git a/src/adc.c b/src/adc.c index 954264e..e7fffbe 100644 --- a/src/adc.c +++ b/src/adc.c @@ -35,8 +35,8 @@ int32_t ts_adc_init(ts_adc_t* adc, spi_dev_t spi, file_t fd) if(retVal == TS_STATUS_OK) { - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_DOWNSAMPLING_ADDR, 1); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_DOWNSAMPLING_ADDR, 1); retVal = hmcad15xx_full_scale_adjust(&adc->adcDev, TS_ADC_FULL_SCALE_ADJUST_DEFAULT); } @@ -64,7 +64,7 @@ int32_t ts_adc_set_channel_conf(ts_adc_t* adc, uint8_t channel, uint8_t input, u } } retVal = hmcad15xx_set_channel_config(&adc->adcDev); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); } } @@ -94,7 +94,7 @@ int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse, int3 } } retVal = hmcad15xx_set_channel_config(&adc->adcDev); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); } if(retVal == TS_STATUS_OK) @@ -158,8 +158,8 @@ int32_t ts_adc_channel_enable(ts_adc_t* adc, uint8_t channel, uint8_t enable) } retVal = hmcad15xx_set_channel_config(&adc->adcDev); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_DATA_CHANNELS_ADDR, shuffleMode << CSR_ADC_HAD1511_DATA_CHANNELS_SHUFFLE_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR, shuffleMode << CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET); } return retVal; @@ -177,7 +177,7 @@ int32_t ts_adc_shutdown(ts_adc_t* adc) if(retVal == TS_STATUS_OK) { hmcad15xx_power_mode(&adc->adcDev, HMCAD15_CH_POWERDN); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); } return retVal; @@ -210,7 +210,7 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res if(TS_STATUS_OK == hmcad15xx_set_sample_mode(&adc->adcDev, sample_rate, data_mode)) { hmcad15xx_set_channel_config(&adc->adcDev); - litepcie_writel(adc->ctrl, CSR_ADC_HAD1511_CONTROL_ADDR, 1 << CSR_ADC_HAD1511_CONTROL_FRAME_RST_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); return TS_STATUS_OK; } else diff --git a/src/gpio.c b/src/gpio.c index b7d85e9..1c5cef4 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -27,3 +27,9 @@ void gpio_clear(gpio_t gpio) uint32_t value = litepcie_readl(gpio.fd, gpio.reg); litepcie_writel(gpio.fd, gpio.reg, (value & ~gpio.bit_mask)); } + +void gpio_group_set(gpio_t gpio, uint32_t set) +{ + uint32_t value = litepcie_readl(gpio.fd, gpio.reg); + litepcie_writel(gpio.fd, gpio.reg, (value | (set & gpio.bit_mask))); +} diff --git a/src/gpio.h b/src/gpio.h index fc02610..b3575f0 100644 --- a/src/gpio.h +++ b/src/gpio.h @@ -28,6 +28,7 @@ typedef struct gpio_s uint32_t gpio_get(gpio_t gpio); void gpio_set(gpio_t gpio); void gpio_clear(gpio_t gpio); +void gpio_group_set(gpio_t gpio, uint32_t set); #ifdef __cplusplus } diff --git a/src/i2c.c b/src/i2c.c index d422ebd..d6ebdb2 100644 --- a/src/i2c.c +++ b/src/i2c.c @@ -14,19 +14,45 @@ #include "i2c.h" #include "util.h" +#define I2C_PHY_SPEED_MODE_REG (0x0000) +#define I2C_MASTER_ACTIVE_REG (0x0004) +#define I2C_MASTER_SETTINGS_REG (0x0008) +#define I2C_MASTER_ADDR_REG (0x000C) +#define I2C_MASTER_RXTX_REG (0x0010) +#define I2C_MASTER_STATUS_REG (0x0014) + +#define I2C_MASTER_SETTINGS_LEN_TX_OFFSET (0) +#define I2C_MASTER_SETTINGS_LEN_RX_OFFSET (8) +#define I2C_MASTER_SETTINGS_RECOVER_OFFSET (16) +#define I2C_MASTER_STATUS_TX_READY_OFFSET (0) +#define I2C_MASTER_STATUS_RX_READY_OFFSET (1) +#define I2C_MASTER_STATUS_NACK_OFFSET (8) +#define I2C_MASTER_STATUS_TX_UNFINISHED_OFFSET (16) +#define I2C_MASTER_STATUS_RX_UNFINISHED_OFFSET (17) + #define I2C_WAIT_TIMEOUT (10000) //I2C Transfer Settings -#define I2C_TX_LEN(x) (((x) & 0x7) << CSR_I2C_MASTER_SETTINGS_LEN_TX_OFFSET) -#define I2C_RX_LEN(x) (((x) & 0x7) << CSR_I2C_MASTER_SETTINGS_LEN_RX_OFFSET) -#define I2C_RECOVER(x) (((x) & 0x1) << CSR_I2C_MASTER_SETTINGS_RECOVER_OFFSET) +#define I2C_TX_LEN(x) (((x) & 0x7) << I2C_MASTER_SETTINGS_LEN_TX_OFFSET) +#define I2C_RX_LEN(x) (((x) & 0x7) << I2C_MASTER_SETTINGS_LEN_RX_OFFSET) +#define I2C_RECOVER(x) (((x) & 0x1) << I2C_MASTER_SETTINGS_RECOVER_OFFSET) //I2C Status -#define I2C_TX_READY(x) (((x) >> CSR_I2C_MASTER_STATUS_TX_READY_OFFSET) & 0x1) -#define I2C_RX_READY(x) (((x) >> CSR_I2C_MASTER_STATUS_RX_READY_OFFSET) & 0x1) -#define I2C_NACK(x) (((x) >> CSR_I2C_MASTER_STATUS_NACK_OFFSET) & 0x1) -#define I2C_TX_PEND(x) (((x) >> CSR_I2C_MASTER_STATUS_TX_UNFINISHED_OFFSET) & 0x1) -#define I2C_RX_PEND(x) (((x) >> CSR_I2C_MASTER_STATUS_RX_UNFINISHED_OFFSET) & 0x1) +#define I2C_TX_READY(x) (((x) >> I2C_MASTER_STATUS_TX_READY_OFFSET) & 0x1) +#define I2C_RX_READY(x) (((x) >> I2C_MASTER_STATUS_RX_READY_OFFSET) & 0x1) +#define I2C_NACK(x) (((x) >> I2C_MASTER_STATUS_NACK_OFFSET) & 0x1) +#define I2C_TX_PEND(x) (((x) >> I2C_MASTER_STATUS_TX_UNFINISHED_OFFSET) & 0x1) +#define I2C_RX_PEND(x) (((x) >> I2C_MASTER_STATUS_RX_UNFINISHED_OFFSET) & 0x1) + +static inline void i2c_reg_write(i2c_t dev, uint32_t reg, uint32_t val) +{ + litepcie_writel(dev.fd, (dev.peripheral_baseaddr + reg), val); +} + +static inline uint32_t i2c_reg_read(i2c_t dev, uint32_t reg) +{ + return litepcie_readl(dev.fd, (dev.peripheral_baseaddr + reg)); +} /** * @brief Enable the I2C core @@ -35,11 +61,11 @@ static inline void i2c_activate(i2c_t device, bool active) { if(active) { - litepcie_writel(device.fd, CSR_I2C_MASTER_ACTIVE_ADDR, 1); + i2c_reg_write(device, I2C_MASTER_ACTIVE_REG, 1); } else { - litepcie_writel(device.fd, CSR_I2C_MASTER_ACTIVE_ADDR, 0); + i2c_reg_write(device, I2C_MASTER_ACTIVE_REG, 0); } } @@ -48,7 +74,7 @@ static inline void i2c_activate(i2c_t device, bool active) */ static inline void i2c_set_addr(i2c_t device) { - litepcie_writel(device.fd, CSR_I2C_MASTER_ADDR_ADDR, device.devAddr); + i2c_reg_write(device, I2C_MASTER_ADDR_REG, device.devAddr); } /** @@ -57,7 +83,7 @@ static inline void i2c_set_addr(i2c_t device) static inline void i2c_wait_tx_ready(i2c_t device) { uint32_t timeout = I2C_WAIT_TIMEOUT; - while(timeout && (I2C_TX_READY(litepcie_readl(device.fd, CSR_I2C_MASTER_STATUS_ADDR)) == 0)) + while(timeout && (I2C_TX_READY(i2c_reg_read(device, I2C_MASTER_STATUS_REG)) == 0)) { NS_DELAY(1000); timeout--; @@ -70,7 +96,7 @@ static inline void i2c_wait_tx_ready(i2c_t device) static inline void i2c_wait_rx_ready(i2c_t device) { uint32_t timeout = I2C_WAIT_TIMEOUT; - while(timeout && (I2C_RX_READY(litepcie_readl(device.fd, CSR_I2C_MASTER_STATUS_ADDR)) == 0)) + while(timeout && (I2C_RX_READY(i2c_reg_read(device, I2C_MASTER_STATUS_REG)) == 0)) { NS_DELAY(1000); timeout--; @@ -89,23 +115,23 @@ static bool i2c_tx(i2c_t device, uint32_t data, uint8_t len) uint32_t i2cStatus; //Write Settings - litepcie_writel(device.fd, CSR_I2C_MASTER_SETTINGS_ADDR, I2C_TX_LEN(len)); + i2c_reg_write(device, I2C_MASTER_SETTINGS_REG, I2C_TX_LEN(len)); //Wait for TX ready i2c_wait_tx_ready(device); //Write TX word - litepcie_writel(device.fd, CSR_I2C_MASTER_RXTX_ADDR, data); + i2c_reg_write(device, I2C_MASTER_RXTX_REG, data); //Wait for RX ready i2c_wait_rx_ready(device); //Get ACK - i2cStatus = litepcie_readl(device.fd, CSR_I2C_MASTER_STATUS_ADDR); + i2cStatus = i2c_reg_read(device, I2C_MASTER_STATUS_REG); ack = I2C_NACK(i2cStatus) ? false : true; //Read the RX word - i2cStatus = litepcie_readl(device.fd, CSR_I2C_MASTER_RXTX_ADDR); + i2cStatus = i2c_reg_read(device, I2C_MASTER_RXTX_REG); return ack; } @@ -122,23 +148,23 @@ static bool i2c_rx(i2c_t device, uint8_t len, uint32_t* pData) bool ack; //Write Settings - litepcie_writel(device.fd, CSR_I2C_MASTER_SETTINGS_ADDR, I2C_RX_LEN(len)); + i2c_reg_write(device, I2C_MASTER_SETTINGS_REG, I2C_RX_LEN(len)); //Wait for TX ready i2c_wait_tx_ready(device); //Write TX word - litepcie_writel(device.fd, CSR_I2C_MASTER_RXTX_ADDR, 0); + i2c_reg_write(device, I2C_MASTER_RXTX_REG, 0); //Wait for RX Ready i2c_wait_rx_ready(device); //Get ACK - status = litepcie_readl(device.fd, CSR_I2C_MASTER_STATUS_ADDR); + status = i2c_reg_read(device, I2C_MASTER_STATUS_REG); ack = I2C_NACK(status) ? false : true; //Read Data word - *pData = litepcie_readl(device.fd, CSR_I2C_MASTER_RXTX_ADDR); + *pData = i2c_reg_read(device, I2C_MASTER_RXTX_REG); return ack; } @@ -162,22 +188,22 @@ void i2c_rate_set(i2c_t device, i2c_rate_t rate) i2c_activate(device, false); //Set Clock Rate - litepcie_writel(device.fd, CSR_I2C_PHY_SPEED_MODE_ADDR, rate); + i2c_reg_write(device, I2C_PHY_SPEED_MODE_REG, rate); } void i2c_reset(i2c_t device) { i2c_activate(device, true); - litepcie_writel(device.fd, CSR_I2C_MASTER_SETTINGS_ADDR, I2C_RECOVER(1)); + i2c_reg_write(device, I2C_MASTER_SETTINGS_REG, I2C_RECOVER(1)); i2c_wait_tx_ready(device); - litepcie_writel(device.fd, CSR_I2C_MASTER_RXTX_ADDR, 0); + i2c_reg_write(device, I2C_MASTER_RXTX_REG, 0); i2c_wait_rx_ready(device); - litepcie_readl(device.fd, CSR_I2C_MASTER_RXTX_ADDR); + i2c_reg_read(device, I2C_MASTER_RXTX_REG); i2c_activate(device, false); } diff --git a/src/i2c.h b/src/i2c.h index 8a8c44f..43a8dab 100644 --- a/src/i2c.h +++ b/src/i2c.h @@ -25,6 +25,7 @@ extern "C" { typedef struct i2c_s { file_t fd; + uint32_t peripheral_baseaddr; uint8_t devAddr; } i2c_t; diff --git a/src/platform.c b/src/platform.c index 1cabfba..25f9d0c 100644 --- a/src/platform.c +++ b/src/platform.c @@ -87,6 +87,19 @@ const mcp_clkgen_conf_t ZL30250_CONF[] = { const uint32_t ZL30250_CONF_SIZE = sizeof(ZL30250_CONF)/sizeof(ZL30250_CONF[0]); +const led_signals_t ts_beta_leds = { + .error = 0, + .ready = 0, + .active = 1 +}; + +const led_signals_t ts_dev_leds = { + .error = 1, + .ready = 2, + .active = 4 +}; + + // A35T/A50T (0x80_0000): // | Address Range | Content | diff --git a/src/platform.h b/src/platform.h index d6abd06..5c2108b 100644 --- a/src/platform.h +++ b/src/platform.h @@ -14,6 +14,9 @@ #include "i2c.h" #include "csr.h" +#define TS_STATUS_LED_ADDR (CSR_DEV_STATUS_LEDS_ADDR) +#define TS_STATUS_LED_COUNT (3) +#define TS_STATUS_LED_MASK ((1 << TS_STATUS_LED_COUNT) - 1) #define TS_ADC_FULL_SCALE_ADJUST_DEFAULT (0x20) /**< Full Scale Adjust set 2V */ #define TS_ADC_CH_COARSE_GAIN_DEFAULT (9) @@ -33,17 +36,22 @@ #define TS_BIAS_RESISTOR_NOMINAL (500) #define TS_PREAMP_INPUT_BIAS_CURRENT_uA (40) -#define TS_SPI_BUS_BASE_ADDR CSR_MAIN_SPI_BASE -#define TS_SPI_BUS_CS_NUM (CSR_MAIN_SPI_CS_SEL_SIZE) +#define TS_SPI_BUS_BASE_ADDR (CSR_SPIBUS_SPI0_CONTROL_ADDR) +#define TS_SPI_BUS_BETA_CS_NUM (5) +#define TS_SPI_BUS_DEV_CS_NUM (4) #define TS_AFE_0_AMP_CS (0) #define TS_AFE_1_AMP_CS (1) #define TS_AFE_2_AMP_CS (2) #define TS_AFE_3_AMP_CS (3) -#define TS_ADC_CS (4) +#define TS_BETA_ADC_CS (4) + +#define TS_ADC_SPI_BUS_BASE_ADDR (CSR_SPIBUS_SPI1_CONTROL_ADDR) +#define TS_ADC_SPI_BUS_CS_NUM (1) +#define TS_ADC_CS (0) -#define TS_I2C_BASE_ADDR CSR_I2C_BASE #define TS_I2C_CLK_RATE I2C_400KHz +#define TS_TRIM_DAC_BUS (CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR) #define TS_TRIM_DAC_I2C_ADDR (0x60) #define TS_TRIM_DAC_DEFAULT (0x800) @@ -52,6 +60,7 @@ #define TS_AFE_2_TRIM_DAC (2) #define TS_AFE_3_TRIM_DAC (3) +#define TS_TRIM_DPOT_BUS (CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR) #define TS_TRIM_DPOT_I2C_ADDR (0x2C) #define TS_TRIM_DPOT_DEFAULT (0x40) @@ -74,6 +83,8 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_PLL_CONF ZL30250_CONF #define TS_PLL_CONF_SIZE ZL30250_CONF_SIZE #else +#define TS_PLL_BUS_BETA CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR +#define TS_PLL_BUS_DEV CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR #define TS_PLL_I2C_ADDR ZL30260_I2C_ADDR #define TS_PLL_CONF ZL30260_CONF #define TS_PLL_CONF_SIZE ZL30260_CONF_SIZE @@ -141,6 +152,16 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_AFE_3_COUPLING_MASK (1 << (CSR_FRONTEND_CONTROL_COUPLING_OFFSET + 3)) +typedef struct led_signals_s { + uint32_t error; + uint32_t ready; + uint32_t active; + // Others TBD +} led_signals_t; + +extern const led_signals_t ts_beta_leds; +extern const led_signals_t ts_dev_leds; + typedef struct flash_layout_s { uint32_t factory_bitstream_start; uint32_t factory_bitstream_end; diff --git a/src/thunderscope.c b/src/thunderscope.c index c7f109d..aecce59 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -16,6 +16,7 @@ #include "ts_calibration.h" #include "ts_channel.h" #include "samples.h" +#include "gpio.h" #include "ts_fw_manager.h" #include "util.h" @@ -34,6 +35,9 @@ typedef struct ts_inst_s tsDeviceInfo_t identity; tsChannelHdl_t pChannel; sampleStream_t samples; + gpio_t status_leds; + const led_signals_t *signals; + bool initialized; ts_fw_manager_t fw; //TBD - Other Instance Data } ts_inst_t; @@ -52,6 +56,7 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) if(testDev != INVALID_HANDLE_VALUE) { info->device_id = devIndex; + info->hw_id = litepcie_readl(testDev, CSR_DEV_STATUS_HW_ID_ADDR); //Copy device identifier for (uint32_t i = 0; i < TS_IDENT_STR_LEN; i++) { @@ -67,46 +72,63 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) return retVal; } -tsHandle_t thunderscopeOpen(uint32_t devIdx) +tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) { ts_inst_t* pInst = calloc(sizeof(ts_inst_t), 1); char devName[TS_IDENT_STR_LEN] = {0}; if(pInst) { + pInst->initialized = false; snprintf(devName, TS_IDENT_STR_LEN, LITEPCIE_CTRL_NAME(%d), devIdx); pInst->ctrl = litepcie_open(devName, FILE_FLAGS); } else { - LOG_ERROR("Litepcie Failed to open device %s", devName); + LOG_ERROR("Litepcie Failed to allocate device"); return NULL; } if(pInst->ctrl == INVALID_HANDLE_VALUE) { - LOG_ERROR("litepcie_open returned Invalid File Handle for device %s", devName); + LOG_ERROR("litepcie_open returned Invalid File Handle for device %d (%s)", devIdx, devName); free(pInst); return NULL; } - - if(TS_STATUS_OK != ts_channel_init(&pInst->pChannel, pInst->ctrl)) + + //Get Device Info + pInst->identity.device_id = devIdx; + pInst->identity.hw_id = litepcie_readl(pInst->ctrl, CSR_DEV_STATUS_HW_ID_ADDR); + //Copy device identifier + for (uint32_t i = 0; i < TS_IDENT_STR_LEN; i++) { - LOG_ERROR("Failed to initialize channels"); - litepcie_close(pInst->ctrl); - free(pInst); - return NULL; + pInst->identity.identity[i] = (char)litepcie_readl(pInst->ctrl, CSR_IDENTIFIER_MEM_BASE + 4 * i); } + //TODO Implement Serial Number + snprintf(pInst->identity.serial_number, TS_IDENT_STR_LEN, "TS00##"); + strncpy(pInst->identity.device_path, devName, TS_IDENT_STR_LEN); - if(TS_STATUS_OK != samples_init(&pInst->samples, devIdx, 0)) + if(!skip_init) { - LOG_ERROR("Failed to initialize samples"); - ts_channel_destroy(pInst->pChannel); - litepcie_close(pInst->ctrl); - free(pInst); - return NULL; + if(TS_STATUS_OK != ts_channel_init(&pInst->pChannel, pInst->ctrl)) + { + LOG_ERROR("Failed to initialize channels"); + litepcie_close(pInst->ctrl); + free(pInst); + return NULL; + } + + if(TS_STATUS_OK != samples_init(&pInst->samples, devIdx, 0)) + { + LOG_ERROR("Failed to initialize samples"); + ts_channel_destroy(pInst->pChannel); + litepcie_close(pInst->ctrl); + free(pInst); + return NULL; + } + pInst->initialized = true; } - + if(TS_STATUS_OK != ts_fw_manager_init(pInst->ctrl, &pInst->fw)) { LOG_ERROR("Failed to initialize spiflash"); @@ -117,6 +139,19 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx) return NULL; } + pInst->status_leds.fd = pInst->ctrl; + pInst->status_leds.reg = TS_STATUS_LED_ADDR; + pInst->status_leds.bit_mask = TS_STATUS_LED_MASK; + if(pInst->identity.hw_id & TS_HW_ID_VALID_MASK) + { + pInst->signals = &ts_dev_leds; + } + else + { + pInst->signals = &ts_beta_leds; + } + gpio_group_set(pInst->status_leds, pInst->signals->ready); + LOG_DEBUG("Opened TS idx %d with handle %p", devIdx, pInst); return (tsHandle_t)pInst; } @@ -130,8 +165,11 @@ int32_t thunderscopeClose(tsHandle_t ts) ts_inst_t* pInst = (ts_inst_t*)ts; - samples_teardown(&pInst->samples); - ts_channel_destroy(pInst->pChannel); + if(pInst->initialized) + { + samples_teardown(&pInst->samples); + ts_channel_destroy(pInst->pChannel); + } litepcie_close(pInst->ctrl); free(pInst); @@ -142,7 +180,7 @@ int32_t thunderscopeChannelConfigGet(tsHandle_t ts, uint32_t channel, tsChannelP { ts_inst_t* pInst = (ts_inst_t*)ts; - if(pInst) + if(pInst && pInst->initialized) { return ts_channel_params_get(pInst->pChannel, channel, conf); } @@ -154,7 +192,7 @@ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelP { ts_inst_t* pInst = (ts_inst_t*)ts; - if(pInst) + if(pInst && pInst->initialized) { return ts_channel_params_set(pInst->pChannel, channel, conf); } @@ -165,7 +203,7 @@ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelP int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* state) { ts_inst_t* pInst = (ts_inst_t*)ts; - if(!state || !pInst) + if(!state || !pInst || !pInst->initialized) { return TS_STATUS_ERROR; } @@ -180,7 +218,7 @@ int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolut { ts_inst_t* pInst = (ts_inst_t*)ts; - if(pInst) + if(pInst && pInst->initialized) { return ts_channel_sample_rate_set(pInst->pChannel, rate, resolution); } @@ -192,60 +230,89 @@ int32_t thunderscopeCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCal { ts_inst_t* pInst = (ts_inst_t*)ts; - if(!pInst) + if(pInst && pInst->initialized) { - return TS_STATUS_ERROR; + return ts_channel_calibration_set(pInst->pChannel, channel, cal); } - - return ts_channel_calibration_set(pInst->pChannel, channel, cal); + + return TS_STATUS_ERROR; } int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal) { ts_inst_t* pInst = (ts_inst_t*)ts; - if(!pInst) + if(pInst && pInst->initialized) { - return TS_STATUS_ERROR; + return ts_channel_calibration_get(pInst->pChannel, channel, cal); } - - return ts_channel_calibration_get(pInst->pChannel, channel, cal); + + return TS_STATUS_ERROR; } int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl) { ts_inst_t* pInst = (ts_inst_t*)ts; - if(!pInst) + if(pInst && pInst->initialized) { - return TS_STATUS_ERROR; + return ts_channel_calibration_manual(pInst->pChannel, channel, ctrl); } - - return ts_channel_calibration_manual(pInst->pChannel, channel, ctrl); + + return TS_STATUS_ERROR; } int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable) { int32_t status = TS_STATUS_OK; ts_inst_t* pInst = (ts_inst_t*)ts; + + if(!pInst || !pInst->initialized) + { + return TS_STATUS_ERROR; + } + status = ts_channel_run(pInst->pChannel, enable); if(status != TS_STATUS_OK) { + gpio_group_set(pInst->status_leds, pInst->signals->error); return status; } + if(enable) + { + gpio_group_set(pInst->status_leds, pInst->signals->active); + } + else + { + gpio_group_set(pInst->status_leds, pInst->signals->ready); + } return samples_enable_set(&pInst->samples, enable); } int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) { ts_inst_t* pInst = (ts_inst_t*)ts; - return samples_get_buffers(&pInst->samples, buffer, len); + if(pInst && pInst->initialized) + { + return samples_get_buffers(&pInst->samples, buffer, len); + } + else + { + return TS_STATUS_ERROR; + } } int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) { ts_inst_t* pInst = (ts_inst_t*)ts; - return ts_fw_manager_user_fw_update(&pInst->fw, bitstream, len); + if(pInst) + { + return ts_fw_manager_user_fw_update(&pInst->fw, bitstream, len); + } + else + { + return TS_STATUS_ERROR; + } } \ No newline at end of file diff --git a/src/ts_channel.c b/src/ts_channel.c index 48e27ed..36a93f0 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -40,6 +40,7 @@ typedef struct ts_channel_s { tsChannelCalibration_t cal; } chan[TS_NUM_CHANNELS]; ts_adc_t adc; + spi_bus_t adcSpibus; spi_bus_t spibus; struct { i2c_t clkGen; @@ -118,6 +119,7 @@ static int32_t ts_channel_health_update(ts_channel_t* pTsHdl); int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) { int32_t retVal = TS_STATUS_OK; + bool betaDevice = false; if(pTsChannels == NULL) { @@ -133,6 +135,12 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) return retVal; } + uint32_t id = litepcie_readl(ts, CSR_DEV_STATUS_HW_ID_ADDR); + if(0 == (id & (1 << CSR_DEV_STATUS_HW_ID_HW_VALID_OFFSET))) + { + betaDevice = true; + } + //Initialize Status pChan->ctrl_handle = ts; pChan->status.adc_lost_buffer_count = 0; @@ -167,6 +175,14 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) pChan->pll.clkGen.fd = ts; pChan->pll.clkGen.devAddr = TS_PLL_I2C_ADDR; + if(betaDevice) + { + pChan->pll.clkGen.peripheral_baseaddr = TS_PLL_BUS_BETA; + } + else + { + pChan->pll.clkGen.peripheral_baseaddr = TS_PLL_BUS_DEV; + } //Set I2C Clock i2c_rate_set(pChan->pll.clkGen, TS_I2C_CLK_RATE); @@ -199,18 +215,44 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) goto channel_init_error; } - i2c_t trimDac = {ts, TS_TRIM_DAC_I2C_ADDR}; - i2c_t trimPot = {ts, TS_TRIM_DPOT_I2C_ADDR}; + i2c_t trimDac = {ts, TS_TRIM_DAC_BUS, TS_TRIM_DAC_I2C_ADDR}; + i2c_t trimPot = {ts, TS_TRIM_DPOT_BUS, TS_TRIM_DPOT_I2C_ADDR}; - retVal = spi_bus_init(&pChan->spibus, ts, - TS_SPI_BUS_BASE_ADDR, TS_SPI_BUS_CS_NUM); + if(betaDevice) + { + retVal = spi_bus_init(&pChan->spibus, ts, + TS_SPI_BUS_BASE_ADDR, TS_SPI_BUS_BETA_CS_NUM); + } + else + { + retVal = spi_bus_init(&pChan->spibus, ts, + TS_SPI_BUS_BASE_ADDR, TS_SPI_BUS_DEV_CS_NUM); + } if(retVal != TS_STATUS_OK) { goto channel_init_error; } + if(!betaDevice) + { + retVal = spi_bus_init(&pChan->adcSpibus, ts, + TS_ADC_SPI_BUS_BASE_ADDR, TS_ADC_SPI_BUS_CS_NUM); + if(retVal != TS_STATUS_OK) + { + goto channel_init_error; + } + } + spi_dev_t adcDev; - retVal = spi_dev_init(&adcDev, &pChan->spibus, TS_ADC_CS); + if(betaDevice) + { + retVal = spi_dev_init(&adcDev, &pChan->spibus, TS_BETA_ADC_CS); + } + else + { + retVal = spi_dev_init(&adcDev, &pChan->adcSpibus, TS_ADC_CS); + } + if(retVal != TS_STATUS_OK) { goto channel_init_error; @@ -737,6 +779,8 @@ static int32_t ts_channel_health_update(ts_channel_t* pTsHdl) pTsHdl->status.sys_health.vcc_int = (uint32_t)(((double)litepcie_readl(pTsHdl->ctrl_handle, CSR_XADC_VCCINT_ADDR) / 4096 * 3)*1000); pTsHdl->status.sys_health.vcc_aux = (uint32_t)(((double)litepcie_readl(pTsHdl->ctrl_handle, CSR_XADC_VCCAUX_ADDR) / 4096 * 3)*1000); pTsHdl->status.sys_health.vcc_bram = (uint32_t)(((double)litepcie_readl(pTsHdl->ctrl_handle, CSR_XADC_VCCBRAM_ADDR) / 4096 * 3)*1000); + pTsHdl->status.sys_health.frontend_power_good = (uint8_t)litepcie_readl(pTsHdl->ctrl_handle, CSR_FRONTEND_STATUS_ADDR) & (1 << CSR_FRONTEND_STATUS_FE_PG_OFFSET); + pTsHdl->status.sys_health.acq_power_good = (uint8_t)litepcie_readl(pTsHdl->ctrl_handle, CSR_ADC_STATUS_ADDR) & (1 << CSR_ADC_STATUS_ACQ_PG_OFFSET); return TS_STATUS_OK; } \ No newline at end of file From 0cac81fdec0300289977fcb73876a33474bd2ba2 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 3 May 2025 12:59:49 -0400 Subject: [PATCH 08/83] Fix pybinding bool arg --- bindings/python/tslitex.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 7ea6dae..f16e1e3 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -78,7 +78,7 @@ cdef extern from "thunderscope.h": int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t* info) - tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) + tsHandle_t thunderscopeOpen(uint32_t devIdx, bint skip_init) int32_t thunderscopeClose(tsHandle_t ts) From 466a8d52077e9698e98426f760ff087cd27d757d Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 4 May 2025 00:23:32 -0400 Subject: [PATCH 09/83] Add io test for dev peripherals and misc cleanup. --- bindings/python/tslitex.pyx | 2 +- example/thunderscope_test.cpp | 119 ++++++++++++++++++++++++++++++---- src/mcp4728.h | 13 +++- src/platform.c | 16 +++-- src/platform.h | 8 +++ 5 files changed, 139 insertions(+), 19 deletions(-) diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index dc3cec8..2842390 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -170,7 +170,7 @@ cdef class Thunderscope: self._sample_rate = 1000000000 self._sample_mode = 256 self._enable = 0 - self._tsHandle = tslitex.thunderscopeOpen(dev_idx, skip_init) + self._tsHandle = tslitex.thunderscopeOpen(dev_idx, skip_init) if self._tsHandle == NULL: raise ValueError(f"Failed to Open Thunderscope Device {dev_idx}", dev_idx) for ch in range(4): diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 980549a..699f764 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -48,6 +48,7 @@ #include "../src/ts_channel.h" #include "../src/samples.h" +#include "../src/platform.h" #include "thunderscope.h" @@ -105,6 +106,11 @@ void configure_frontend_ldo(file_t fd, uint32_t enable) { litepcie_writel(fd, CSR_FRONTEND_CONTROL_ADDR, control_value); } +bool query_frontend_pg(file_t fd) { + uint32_t status_value = litepcie_readl(fd, CSR_FRONTEND_STATUS_ADDR); + return (status_value & (1 << CSR_FRONTEND_STATUS_FE_PG_OFFSET)); +} + void configure_adc_ldo(file_t fd, uint32_t enable) { uint32_t control_value = litepcie_readl(fd, CSR_ADC_CONTROL_ADDR); control_value &= ~(1 * ADC_CONTROL_LDO_EN); @@ -112,6 +118,11 @@ void configure_adc_ldo(file_t fd, uint32_t enable) { litepcie_writel(fd, CSR_ADC_CONTROL_ADDR, control_value); } +bool query_adc_pg(file_t fd) { + uint32_t status_value = litepcie_readl(fd, CSR_ADC_STATUS_ADDR); + return (status_value & (1 << CSR_ADC_STATUS_ACQ_PG_OFFSET)); +} + void configure_pll_en(file_t fd, uint32_t enable) { uint32_t control_value = litepcie_readl(fd, CSR_ADC_CONTROL_ADDR); control_value &= ~(1 * ADC_CONTROL_PLL_EN); @@ -119,11 +130,8 @@ void configure_pll_en(file_t fd, uint32_t enable) { litepcie_writel(fd, CSR_ADC_CONTROL_ADDR, control_value); } -void control_led(file_t fd, uint32_t enable) { - uint32_t control_value = litepcie_readl(fd, CSR_DEV_STATUS_LEDS_ADDR); - control_value &= ~(1 * AFE_STATUS_LDO_PWR_GOOD); - control_value |= (enable * AFE_STATUS_LDO_PWR_GOOD); - litepcie_writel(fd, CSR_DEV_STATUS_LEDS_ADDR, control_value); +void control_led(file_t fd, uint8_t val) { + litepcie_writel(fd, CSR_DEV_STATUS_LEDS_ADDR, (uint32_t)val); } static uint32_t read_flash_word(file_t fd, uint32_t flash_addr) @@ -151,7 +159,7 @@ auto awake_time() return now() + 500ms; } -static void test_io(file_t fd) +static void test_io(file_t fd, bool isBeta) { printf("\x1b[1m[> Scratch register test:\x1b[0m\n"); printf("-------------------------\n"); @@ -170,9 +178,21 @@ static void test_io(file_t fd) printf("Enabling frontend LDO:\n"); configure_frontend_ldo(fd, 1); + std::this_thread::sleep_until(awake_time()); + if(query_frontend_pg(fd)) + { + printf("\tFrontend Power Good\n"); + } + printf("Enabling ADC LDO:\n"); configure_adc_ldo(fd, 1); + std::this_thread::sleep_until(awake_time()); + if(query_adc_pg(fd)) + { + printf("\tADC Power Good\n"); + } + printf("Disabling PLL EN & waiting 500ms:\n"); configure_pll_en(fd, 0); @@ -181,11 +201,37 @@ static void test_io(file_t fd) printf("Enabling PLL EN:\n"); configure_pll_en(fd, 1); + //Cycle LEDs + const led_signals_t *leds; + if(isBeta) + { + leds = &ts_beta_leds; + } + else + { + leds = &ts_dev_leds; + } + + printf("Set READY LED\n"); + control_led(fd, leds->ready); + using std::chrono::operator"" s; + std::this_thread::sleep_for(3s); + printf("Set ERROR LED\n"); + control_led(fd, leds->error); + using std::chrono::operator"" s; + std::this_thread::sleep_for(3s); + printf("Set ACTIVE LED\n"); + control_led(fd, leds->active); + using std::chrono::operator"" s; + std::this_thread::sleep_for(3s); + + control_led(fd, leds->disabled); + i2c_t i2cDev; i2cDev.fd = fd; - i2cDev.peripheral_baseaddr = CSR_I2CBUS_BASE; + i2cDev.peripheral_baseaddr = CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR; i2c_rate_set(i2cDev, I2C_400KHz); - + printf("\nScan I2C Bus 0:\n"); for (unsigned char addr = 0; addr < 0x80; addr++) { i2cDev.devAddr = addr; bool result = i2c_poll(i2cDev); @@ -199,20 +245,64 @@ static void test_io(file_t fd) printf(" --"); } } + + if(!isBeta) + { + i2cDev.peripheral_baseaddr = CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR; + i2c_rate_set(i2cDev, I2C_400KHz); + printf("\nScan I2C Bus 1:\n"); + for (unsigned char addr = 0; addr < 0x80; addr++) { + i2cDev.devAddr = addr; + bool result = i2c_poll(i2cDev); + if (addr % 0x10 == 0) { + printf("\n0x%02X", addr); + } + if (result) { + printf(" %02X", addr); + } + else { + printf(" --"); + } + } + } - spi_bus_t spimaster; - spi_bus_init(&spimaster, fd, CSR_SPIBUS_BASE, CSR_SPIBUS_SPI0_CS_SEL_SIZE); + spi_bus_t spibus0; + spi_bus_init(&spibus0, fd, CSR_SPIBUS_BASE, CSR_SPIBUS_SPI0_CS_SEL_SIZE); uint8_t data[2] = {0x01, 0x02}; for (int i = 0; i < CSR_SPIBUS_SPI0_CS_SEL_SIZE; i++) { spi_dev_t spiDev; - spi_dev_init(&spiDev, &spimaster, i); + spi_dev_init(&spiDev, &spibus0, i); for (int reg = 0; reg < 10; reg++) { spi_write(spiDev, reg, data, 2); spi_busy_wait(spiDev); } } + + if(!isBeta) + { + spi_bus_t spibus1; + spi_bus_init(&spibus1, fd, CSR_SPIBUS_SPI1_CONTROL_ADDR, CSR_SPIBUS_SPI1_CS_SEL_SIZE); + + uint8_t data[2] = {0x01, 0x02}; + + for (int i = 0; i < CSR_SPIBUS_SPI1_CS_SEL_SIZE; i++) { + spi_dev_t spiDev; + spi_dev_init(&spiDev, &spibus1, i); + for (int reg = 0; reg < 10; reg++) { + spi_write(spiDev, reg, data, 2); + spi_busy_wait(spiDev); + } + } + } + + printf("\nPress ENTER to continue...\n"); + std::cin.ignore(1); + + // Cleanup + configure_frontend_ldo(fd, 0); + configure_adc_ldo(fd, 0); } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, @@ -476,6 +566,10 @@ static void print_help(void) { printf("TS Test Util Usage:\r\n"); printf("\t io - run I/O Test\r\n"); + printf("\t flash - Test Flash device\r\n"); + printf("\t\t read Read a word from flash\r\n"); + printf("\t\t dump Dump the contents of flash to a file\r\n"); + printf("\t\t test Destructive Flash erase/read/write test\r\n"); printf("\t capture - run Sample Capture Test\r\n"); printf("\t\t -d Device Index\r\n"); printf("\t\t -c Channel bitmap\r\n"); @@ -658,13 +752,14 @@ int main(int argc, char** argv) // Run Example IO if(0 == strcmp(arg, "io")) { - test_io(fd); + test_io(fd, ((hw_info & TS_HW_ID_VALID_MASK) == 0)); } // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_mV, offset_mV, ac_couple, term); } + // Flash test else if(0 == strcmp(arg, "flash")) { flash_test(argv[argCount+1], fd); diff --git a/src/mcp4728.h b/src/mcp4728.h index 7d91356..e9444a3 100644 --- a/src/mcp4728.h +++ b/src/mcp4728.h @@ -6,6 +6,12 @@ * Copyright (C) 2024 / Nate Meyer / nate.devel@gmail.com * */ +#ifndef _MCP4728_H_ +#define _MCP4728_H_ + +#ifdef __cplusplus +extern "C" { +#endif #include #include "i2c.h" @@ -49,4 +55,9 @@ typedef struct Mcp4728ChannelConfig_s * * @return int32_t TS_STATUS_OK on success, else error */ -int32_t mcp4728_channel_set(i2c_t dev, uint8_t channel, Mcp4728ChannelConfig_t conf); \ No newline at end of file +int32_t mcp4728_channel_set(i2c_t dev, uint8_t channel, Mcp4728ChannelConfig_t conf); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/platform.c b/src/platform.c index 25f9d0c..a937be0 100644 --- a/src/platform.c +++ b/src/platform.c @@ -86,17 +86,23 @@ const mcp_clkgen_conf_t ZL30250_CONF[] = { const uint32_t ZL30250_CONF_SIZE = sizeof(ZL30250_CONF)/sizeof(ZL30250_CONF[0]); - +// +// Bitmasks for LED indicators +// +//Beta hardware has a single LED that is active low const led_signals_t ts_beta_leds = { - .error = 0, - .ready = 0, - .active = 1 + .error = 1, + .ready = 1, + .active = 0, + .disabled = 1 }; +//Production hardware has 3 active high LEDs (RGB) const led_signals_t ts_dev_leds = { .error = 1, .ready = 2, - .active = 4 + .active = 4, + .disabled = 0 }; diff --git a/src/platform.h b/src/platform.h index 5c2108b..0f98060 100644 --- a/src/platform.h +++ b/src/platform.h @@ -8,6 +8,10 @@ #ifndef _PLATFORM_H_ #define _PLATFORM_H_ +#ifdef __cplusplus +extern "C" { +#endif + #include "ts_common.h" #include "mcp_clkgen.h" #include "mcp_zl3026x.h" @@ -157,6 +161,7 @@ typedef struct led_signals_s { uint32_t ready; uint32_t active; // Others TBD + uint32_t disabled; } led_signals_t; extern const led_signals_t ts_beta_leds; @@ -181,4 +186,7 @@ extern const flash_layout_t ts_64Mb_layout; #define TS_FLASH_64M_MFG (0xC2) //Macronix #define TS_FLASH_64M_ID (0x2537) //64Mb SPI Flash +#ifdef __cplusplus +} +#endif #endif \ No newline at end of file From 5168b20a5ef6b8456fb2b92b7eaefdaaded3fb20 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 13 May 2025 20:03:38 -0400 Subject: [PATCH 10/83] Add example control util for manually setting channel config. --- README.md | 3 +- example/tsControl.py | 327 +++++++++++++++++++++++++++++++++++++++++++ src/ts_channel.c | 14 -- 3 files changed, 329 insertions(+), 15 deletions(-) create mode 100644 example/tsControl.py diff --git a/README.md b/README.md index 3dab365..3a5396f 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,11 @@ To build the python bindings, you must first have pipx and cython installed. The > pip install -r requirements.txt ``` -Now you can build the `PyBindings` target with cmake +Now you can build the `PyBindings` target with cmake. If you get an error about some module not being installed, recreate the cmake cache with `cmake --fresh ..` ```bash > cd build/ +> cmake --fresh .. > cmake --build . -t PyBindings ``` diff --git a/example/tsControl.py b/example/tsControl.py new file mode 100644 index 0000000..52b7551 --- /dev/null +++ b/example/tsControl.py @@ -0,0 +1,327 @@ +""" SPDX-License-Identifier: BSD-2-Clause +@file tsControl.py + +@brief Thunderscope Control GUI + +@details +A graphical user interface (GUI) application for controlling and monitoring a Thunderscope device. +This script provides functionality to connect to a Thunderscope, configure its channels, and view its status. +It uses the Tkinter library for the GUI and interacts with the Thunderscope through the tslitex library. + +Features: +- Device connection and disconnection management. +- Channel configuration with adjustable parameters. +- Real-time status monitoring of the Thunderscope device. + +@copyright (C) 2025 / Nate Meyer / nate.devel@gmail.com +""" + +import tslitex +import tkinter as tk +from tkinter import ttk + +class TsDelegate: + """ + A delegate class to handle events and actions for the TsControl. + """ + + def __init__(self, parent): + self.parent = parent + self.ts_inst = None + self.dev_list = [] + + def on_channel_apply(self, channel, params): + print(f"Applying settings for Channel {channel}: {params}") + if self.ts_inst: + self.ts_inst.channel[channel-1].ManualCtrl(params) + + def get_devices(self): + print("Retrieving devices...") + # Return a list of devices (this can be replaced with actual logic to fetch devices) + self.dev_list = [] + idx = 0 + while True: + res, dev_info = tslitex.ThunderscopeListDevs(idx) + if res == -1: + break + else: + print(f"Found device {idx}: {dev_info}") + self.dev_list.append(f"{idx}: Thunderscope {dev_info['device_path'].decode('utf-8')}") + idx += 1 + return self.dev_list + + def connect_device(self, device): + # Handle the connect event + print(f"Connecting to device: {device}") + self.ts_inst = tslitex.Thunderscope(dev_idx=device) + self.update_status(f"Connected: Thunderscope {device}") + # Change the connect button to say disconnect + connect_button = self.parent.nametowidget('top_frame.connect_button') + connect_button.config(text="Disconnect", command=self.on_close) + + # Reset fields for all ChannelCtrl instances + for tab in self.parent.nametowidget('!notebook').winfo_children(): + if isinstance(tab, ChannelCtrl): + tab.reset_fields() + + def on_close(self): + # Handle the window close event + if self.ts_inst: + print("Deleting ts_inst...") + del self.ts_inst + self.ts_inst = None + # Change the connect button to say connect + connect_button = self.parent.nametowidget('top_frame.connect_button') + connect_button.config(text="Connect", command=lambda: self.connect_device(self.parent.nametowidget('top_frame.device_combobox').current())) + self.update_status("Disconnected") + + def update_status(self, message): + status_bar = self.parent.nametowidget('status_bar') + status_bar.config(text=message) + + +class ChannelCtrl(ttk.Frame): + """ + A class to create a channel control interface using Tkinter. + """ + + def __init__(self, parent, channel, on_channel_apply_callback): + super().__init__(parent) + self.channel = channel + self.on_channel_apply_callback = on_channel_apply_callback + self.enable = tk.BooleanVar(value=False) + self.atten = tk.BooleanVar(value=False) + self.dc_couple = tk.BooleanVar(value=False) + self.termination = tk.BooleanVar(value=False) + self.pga_high_gain = tk.BooleanVar(value=False) + self.pga_bw = tk.IntVar(value=6) + self.pga_atten = tk.IntVar(value=10) + self.trim_dac = tk.IntVar(value=2048) + self.trim_pot = tk.IntVar(value=0x40) + self.create_widgets() + + def create_widgets(self): + # Create a frame + frame = ttk.Frame(self, padding=10) + frame.pack(expand=True, fill="both") + + # Add Checkboxes + tk.Checkbutton(frame, text="Attenuation", variable=self.atten).grid(row=2, column=0, sticky="w", pady=5) + tk.Checkbutton(frame, text="Termination", variable=self.termination).grid(row=3, column=0, sticky="w", pady=5) + tk.Checkbutton(frame, text="DC Coupling", variable=self.dc_couple).grid(row=4, column=0, sticky="w", pady=5) + tk.Checkbutton(frame, text="PGA High-Gain", variable=self.pga_high_gain).grid(row=5, column=0, sticky="w", pady=5) + + # Add Text Boxes with Range Labels + ttk.Label(frame, text="Trim DAC:").grid(row=2, column=1, sticky="w", pady=5) + trim_dac_entry = ttk.Entry(frame, textvariable=self.trim_dac, width=10) + trim_dac_entry.grid(row=2, column=2, pady=5, padx=5) + ttk.Label(frame, text="(Range: 0 - 4095)").grid(row=2, column=3, sticky="w", padx=5) + + ttk.Label(frame, text="Trim Pot:").grid(row=3, column=1, sticky="w", pady=5) + trim_pot_entry = ttk.Entry(frame, textvariable=self.trim_pot, width=10) + trim_pot_entry.grid(row=3, column=2, pady=5, padx=5) + ttk.Label(frame, text="(Range: 0 - 255)").grid(row=3, column=3, sticky="w", padx=5) + + ttk.Label(frame, text="PGA Attenuation:").grid(row=4, column=1, sticky="w", pady=5) + pga_atten_entry = ttk.Entry(frame, textvariable=self.pga_atten, width=10) + pga_atten_entry.grid(row=4, column=2, pady=5, padx=5) + ttk.Label(frame, text="(Range: 0 - 10)").grid(row=4, column=3, sticky="w", padx=5) + + ttk.Label(frame, text="PGA Bandwidth:").grid(row=5, column=1, sticky="w", pady=5) + pga_bw_entry = ttk.Entry(frame, textvariable=self.pga_bw, width=10) + pga_bw_entry.grid(row=5, column=2, pady=5, padx=5) + ttk.Label(frame, text="(Range: 0 - 6)").grid(row=5, column=3, sticky="w", padx=5) + + # Add a Button + ttk.Button(frame, text="Apply", command=self.submit).grid(row=6, column=0, pady=10) + + def submit(self): + # Validate and adjust entry values + dac = int(self.trim_dac.get()) + if dac < 0: + dac = 0 + self.trim_dac.set(dac) + elif dac > 4095: + dac = 4095 + self.trim_dac.set(dac) + + dpot = int(self.trim_pot.get()) + if dpot < 0: + dpot = 0 + self.trim_pot.set(dpot) + elif dpot > 255: + dpot = 255 + self.trim_pot.set(dpot) + + pga_atten = int(self.pga_atten.get()) + if pga_atten < 0: + pga_atten = 0 + self.pga_atten.set(pga_atten) + elif pga_atten > 10: + pga_atten = 10 + self.pga_atten.set(pga_atten) + + pga_bw = int(self.pga_bw.get()) + if pga_bw < 0: + pga_bw = 0 + self.pga_bw.set(pga_bw) + elif pga_bw > 6: + pga_bw = 6 + self.pga_bw.set(pga_bw) + + # Collect parameters from the checkbuttons and entry fields + params = { + "atten": self.atten.get(), + "term": self.termination.get(), + "dc_couple": self.dc_couple.get(), + "dac": dac, + "dpot": dpot, + "pga_high_gain": self.pga_high_gain.get(), + "pga_atten": pga_atten, + "pga_bw": pga_bw + } + + # Call the provided callback with the channel and parameters + if self.on_channel_apply_callback: + self.on_channel_apply_callback(self.channel, params) + + def reset_fields(self): + # Reset checkboxes + self.atten.set(False) + self.dc_couple.set(False) + self.termination.set(False) + self.pga_high_gain.set(False) + + # Reset entry fields to their default values + self.trim_dac.set(2048) + self.trim_pot.set(0x40) + self.pga_atten.set(10) + self.pga_bw.set(6) + + +# Create the main application window +root = tk.Tk() +root.title("Thunderscope Channel Tester") +root.geometry("600x450") # Set the window size + +# Create an instance of TsDelegate +delegate = TsDelegate(root) +root.protocol("WM_DELETE_WINDOW", lambda: (delegate.on_close(), root.destroy())) + + +# Create a frame for the drop-down menu and button +top_frame = ttk.Frame(root, padding=10, name="top_frame") +top_frame.pack(side="top", fill="x") + +# Add a drop-down menu (Combobox) to the top frame +ttk.Label(top_frame, text="Select Device:").pack(side="left", padx=5) +device_combobox = ttk.Combobox(top_frame, values=delegate.get_devices(), state="readonly", name="device_combobox") +device_combobox.pack(side="left", padx=5) + +# Set the width of the combobox to increase its size +device_combobox.config(width=40) + +# Add a callback to refresh the device list when the dropdown menu is clicked +def refresh_device_list(event): + device_combobox["values"] = delegate.get_devices() + +device_combobox.bind("", refresh_device_list) + +# Add a "Connect" button to the top frame +connect_button = ttk.Button( + top_frame, + text="Connect", + command=lambda: delegate.connect_device(device_combobox.current()), + name="connect_button" +) +connect_button.pack(side="left", padx=5) + + +# Create a style for the notebook +style = ttk.Style() +style.configure("TNotebook.Tab", padding=[10, 5]) # Add padding to tabs + +# Update the notebook style to position tabs on the left side +style.configure("TNotebook", tabposition="wn") +style.configure("TNotebook", expand=True) # Configure the notebook tabs to evenly fill the width of the window +style.map("TNotebook.Tab", expand=[("selected", 1), ("!selected", 1)]) # Stretch tabs evenly + + +# Create a Notebook widget (for tabs) +notebook = ttk.Notebook(root, style="TNotebook") + + +# Modify the status tab to display labels and number fields for each key in tsScopeState_t +def create_status_tab(notebook, delegate): + status_tab = ttk.Frame(notebook) + + # Create a dictionary to hold labels for each key in tsScopeState_t + status_labels = {} + + # Define the keys from tsScopeState_t + keys = [ + "adc_sample_rate", "adc_sample_bits", "adc_sample_resolution", "adc_lost_buffer_count", + "flags", "adc_state", "power_state", "pll_state", "afe_state", + "sys_health.temp_c", "sys_health.vcc_int", "sys_health.vcc_aux", + "sys_health.vcc_bram", "sys_health.frontend_power_good", "sys_health.acq_power_good" + ] + + # Create labels and fields for each key + for idx, key in enumerate(keys): + ttk.Label(status_tab, text=key + ":").grid(row=idx, column=0, sticky="w", padx=10, pady=2) + status_labels[key] = ttk.Label(status_tab, text="N/A") + status_labels[key].grid(row=idx, column=1, sticky="w", padx=10, pady=2) + + def update_status(): + if delegate.ts_inst: + status = delegate.ts_inst.Status() + for key in keys: + # Handle nested keys like sys_health.temp_c + value = status + for part in key.split('.'): + value = value.get(part, "N/A") + status_labels[key].config(text=value) + else: + for key in keys: + status_labels[key].config(text="N/A") + + # Periodically update the status only when the tab is selected + def periodic_update(): + if notebook.index(notebook.select()) == 0: # Check if tab0 is selected + update_status() + status_tab.after(1000, periodic_update) + + # Bind events to start and stop updates + def on_tab_changed(event): + if notebook.index(notebook.select()) == 0: # Tab0 selected + periodic_update() + + notebook.bind("<>", on_tab_changed) + return status_tab + +# Add the status tab to the notebook +tab0 = create_status_tab(notebook, delegate) +notebook.add(tab0, text="Status") + + +# Create frames for each tab +tab1 = ChannelCtrl(notebook, 1, delegate.on_channel_apply) +tab2 = ChannelCtrl(notebook, 2, delegate.on_channel_apply) +tab3 = ChannelCtrl(notebook, 3, delegate.on_channel_apply) +tab4 = ChannelCtrl(notebook, 4, delegate.on_channel_apply) + +# Add tabs to the notebook +notebook.add(tab1, text="Chan 1") +notebook.add(tab2, text="Chan 2") +notebook.add(tab3, text="Chan 3") +notebook.add(tab4, text="Chan 4") + +# Pack the notebook widget into the main window +notebook.pack(expand=True, fill="both") + +# Add a status bar to the bottom of the window +status_bar = ttk.Label(root, text="Disconnected", relief="sunken", anchor="w", name="status_bar") +status_bar.pack(side="bottom", fill="x") + +# Start the Tkinter event loop +root.mainloop() diff --git a/src/ts_channel.c b/src/ts_channel.c index 36a93f0..5221bcf 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -676,20 +676,6 @@ int32_t ts_channel_calibration_manual(tsChannelHdl_t tsChannels, uint32_t chanId return TS_INVALID_PARAM; } - - //Set AFE Bandwidth - retVal = ts_afe_set_bw_filter(&ts->chan[chanIdx].afe, ctrl.pga_bw); - if(retVal > 0) - { - LOG_DEBUG("Channel %d AFE BW set to %i MHz", chanIdx, retVal); - } - else - { - LOG_ERROR("Unable to set Channel %d bandwidth %d", chanIdx, retVal); - return TS_INVALID_PARAM; - } - - //Set AC/DC Coupling if(TS_STATUS_OK == ts_afe_coupling_control(&ts->chan[chanIdx].afe, ctrl.dc_couple == 1 ? TS_COUPLE_DC : TS_COUPLE_AC )) From 4e7d1ef546ef4e23cbcdfafe5d6034aa6374a038 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 17 May 2025 23:45:12 -0400 Subject: [PATCH 11/83] Update python module to add numpy dependency and remove 'py' prefix --- bindings/python/pyproject.toml.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/python/pyproject.toml.in b/bindings/python/pyproject.toml.in index da621e1..48f854f 100644 --- a/bindings/python/pyproject.toml.in +++ b/bindings/python/pyproject.toml.in @@ -3,12 +3,13 @@ requires = ["cython", "numpy", "setuptools"] build-backend = "setuptools.build_meta" [project] -name = "pytslitex" +name = "tslitex" version = "0.0.1" license = {text = "BSD-2-Clause"} authors = [ {name = "Nate Meyer", email = "nate.devel@gmail.com"}, ] +dependencies = ["numpy"] [tool.setuptools] packages = ["tslitex"] From d381a41e4dab2a913e47eb33afb4fcbd34eb3591 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 17 May 2025 23:54:22 -0400 Subject: [PATCH 12/83] Make tsControl example executable --- example/tsControl.py | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 example/tsControl.py diff --git a/example/tsControl.py b/example/tsControl.py old mode 100644 new mode 100755 index 52b7551..f8687b2 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """ SPDX-License-Identifier: BSD-2-Clause @file tsControl.py From 11af87ba7549a8a843b3bc209005dc3b65d367fa Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 20 May 2025 01:26:02 -0400 Subject: [PATCH 13/83] Add API to query firmware update progress --- bindings/python/tslitex.pxd | 6 ++-- bindings/python/tslitex.pyx | 17 +++++++++-- include/thunderscope.h | 9 ++++++ src/spiflash.c | 12 ++++++++ src/spiflash.h | 4 +++ src/thunderscope.c | 14 +++++++++ src/ts_fw_manager.c | 58 ++++++++++++++++++++++++++++++++++++- src/ts_fw_manager.h | 14 +++++++++ 8 files changed, 128 insertions(+), 6 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index f16e1e3..c328dc5 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -92,6 +92,8 @@ cdef extern from "thunderscope.h": int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable) - int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) + int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) nogil - int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) + int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) nogil + + int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index 2842390..a2e4763 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -163,7 +163,7 @@ cdef class Thunderscope: cdef tslitex.tsHandle_t _tsHandle cdef public object channel - def __cinit__(self, dev_idx: int): + def __cinit__(self, dev_idx: int, skip_init:bool = False): self.channel = [] def __init__(self, dev_idx: int, skip_init:bool = False): @@ -192,7 +192,16 @@ cdef class Thunderscope: raise TypeError(f"bitfile arg must be 'bytes' type") cdef uint32_t file_len = len(bitfile) cdef char* pFile = bitfile - return tslitex.thunderscopeFwUpdate(self._tsHandle, pFile, file_len) + cdef int32_t status + with nogil: + status = tslitex.thunderscopeFwUpdate(self._tsHandle, pFile, file_len) + return status + + @property + def firmwareProgress(self): + cdef uint32_t progress + tslitex.thunderscopeGetFwProgress(self._tsHandle, &progress) + return progress @property def SampleRate(self): @@ -221,6 +230,8 @@ cdef class Thunderscope: raise ValueError(f"Unable to set Thunderscope Enable to {enable}") def Read(self, uint8_t[:] data not None, dataLen: int): - readLen = tslitex.thunderscopeRead(self._tsHandle, &data[0], dataLen) + cdef uint32_t dlen = dataLen + with nogil: + readLen = tslitex.thunderscopeRead(self._tsHandle, &data[0], dlen) return readLen diff --git a/include/thunderscope.h b/include/thunderscope.h index 80d129c..e80e572 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -115,6 +115,15 @@ int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len); */ int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len); +/** + * @brief Get the current progress of the firmware update + * + * @param ts Handle to the Thunderscope device + * @param progress Pointer to a variable to store the progress percentage + * @return int32_t TS_STATUS_OK if the progress was retrieved successfully, or a negative error code + */ +int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress); + #ifdef __cplusplus } #endif diff --git a/src/spiflash.c b/src/spiflash.c index 05e756d..37b39a4 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -268,6 +268,12 @@ int32_t spiflash_erase(spiflash_dev_t* dev, uint32_t addr, uint32_t len) return TS_STATUS_ERROR; } } + + //Report progress + if(dev->op_progress != NULL) + { + dev->op_progress(dev->op_progress_ctx, SPI_FLASH_ERASE_SIZE, len); + } } spiflash_write_disable(dev->fd); @@ -300,6 +306,12 @@ int32_t spiflash_write(spiflash_dev_t* dev, uint32_t addr, const uint8_t *pData, } } + //Report progress + if(dev->op_progress != NULL) + { + dev->op_progress(dev->op_progress_ctx, w_len, len); + } + offset += w_len; w_len = min(len-offset, SPI_FLASH_PROG_SIZE); res = offset; diff --git a/src/spiflash.h b/src/spiflash.h index bcd11c3..5b02ca1 100644 --- a/src/spiflash.h +++ b/src/spiflash.h @@ -18,6 +18,8 @@ extern "C" { #include "ts_common.h" #include "liblitepcie.h" +typedef void (*spiflash_progress_cb_t)(void* ctx, uint32_t work_done, uint32_t work_total); + typedef struct spiflash_ops_s { uint8_t read; uint8_t program; @@ -30,6 +32,8 @@ typedef struct spiflash_dev_s { uint8_t mfg_code; uint16_t part_id; spiflash_ops_t ops; + spiflash_progress_cb_t op_progress; + void* op_progress_ctx; } spiflash_dev_t; /** diff --git a/src/thunderscope.c b/src/thunderscope.c index aecce59..caa1ada 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -315,4 +315,18 @@ int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) { return TS_STATUS_ERROR; } +} + + +int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_get_progress(&pInst->fw, progress); + } + else + { + return TS_STATUS_ERROR; + } } \ No newline at end of file diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index 8e2899e..37042e8 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -34,6 +34,8 @@ static const struct { {0, ""} }; +static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_total); + static uint32_t ts_fw_get_idcode(file_t fd) { uint32_t code; @@ -134,7 +136,16 @@ int32_t ts_fw_manager_init(file_t fd, ts_fw_manager_t* mngr) return TS_STATUS_ERROR; } //Initialize SPI Flash - spiflash_init(fd, &mngr->flash_dev); + if(TS_STATUS_OK == spiflash_init(fd, &mngr->flash_dev)) + { + mngr->flash_dev.op_progress = ts_fw_progress_update; + mngr->flash_dev.op_progress_ctx = mngr; + } + else + { + LOG_ERROR("Failed to initialize SPI Flash"); + return TS_STATUS_ERROR; + } //Set partition Table if( mngr->flash_dev.mfg_code == TS_FLASH_256M_MFG) @@ -157,12 +168,16 @@ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_str // Verify Bitstream and Strip Header Info uint32_t bin_length = 0; const char* part_name = NULL; + atomic_store(&mngr->fw_progress, 0); const char* bin_start = ts_fw_parse_bit_header(file_stream, &part_name, &bin_length); if(bin_length > len) { LOG_ERROR("INVALID length in bitstream header (%u > %u)", bin_length, len); return TS_STATUS_ERROR; } + LOG_DEBUG("Bitstream Length: %u", bin_length); + + mngr->fw_progress_max = bin_length*2; // Verify FPGA Part // Get IDCODE from TS @@ -210,6 +225,8 @@ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_str return TS_STATUS_ERROR; } + atomic_store(&mngr->fw_progress, bin_length); + // Program New Bitstream if(bin_length != spiflash_write(&mngr->flash_dev, mngr->partition_table->user_bitstream_start, bin_start, bin_length)) { @@ -217,6 +234,36 @@ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_str return TS_STATUS_ERROR; } + atomic_store(&mngr->fw_progress, mngr->fw_progress_max); + + return TS_STATUS_OK; +} + +int32_t ts_fw_manager_get_progress(ts_fw_manager_t* mngr, uint32_t* progress) +{ + uint32_t current_progress = 0; + + // Get Progress of Flash Write + if(mngr == NULL || progress == NULL) + { + LOG_ERROR("Invalid manager handle"); + return TS_STATUS_ERROR; + } + + if(mngr->fw_progress_max == 0) + { + LOG_ERROR("Firmware Update not in progress"); + } + else if (current_progress < mngr->fw_progress_max) + { + current_progress = (mngr->fw_progress * 100) / mngr->fw_progress_max; + } + else + { + current_progress = 100; + } + + *progress = current_progress; return TS_STATUS_OK; } @@ -244,3 +291,12 @@ int32_t ts_fw_manager_factory_cal_get(ts_fw_manager_t* mngr, const char* file_st return TS_STATUS_OK; } + +static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_total) +{ + ts_fw_manager_t* mngr = (ts_fw_manager_t*)ctx; + if(mngr != NULL) + { + atomic_fetch_add(&mngr->fw_progress, work_done); + } +} \ No newline at end of file diff --git a/src/ts_fw_manager.h b/src/ts_fw_manager.h index 0d4e194..f182de1 100644 --- a/src/ts_fw_manager.h +++ b/src/ts_fw_manager.h @@ -16,9 +16,13 @@ extern "C" { #include "spiflash.h" #include "platform.h" +#include + typedef struct ts_fw_manager_s { spiflash_dev_t flash_dev; const flash_layout_t* partition_table; + _Atomic uint32_t fw_progress; + uint32_t fw_progress_max; } ts_fw_manager_t; /** @@ -42,6 +46,16 @@ int32_t ts_fw_manager_init(file_t fd, ts_fw_manager_t* mngr); */ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_stream, uint32_t len); +/** + * @brief Get the current progress of the firmware update + * + * @param mngr Pointer to a manager instance + * @param progress Pointer to a variable to store the progress percentage + * + * @return TS_STATUS_OK on success. + */ +int32_t ts_fw_manager_get_progress(ts_fw_manager_t* mngr, uint32_t* progress); + /** * @brief Read the User Calibration data from flash * From e77e98e41782014a92277af848ab3b78b94da95e Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 20 May 2025 20:20:49 -0400 Subject: [PATCH 14/83] Fix issues found in dev testing for TB/PCIe label and SPI Flash ID --- example/thunderscope_fw.cpp | 2 +- example/thunderscope_test.cpp | 2 +- src/spiflash.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 102c260..67da820 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -151,7 +151,7 @@ int main(int argc, char** argv) if(infos.hw_id & TS_HW_ID_VALID_MASK) { printf("HW Rev %02d - %s\n", infos.hw_id & TS_HW_ID_REV_MASK, - (infos.hw_id & TS_HW_ID_VARIANT_MASK) ? "PCIe" : "TB" ); + (infos.hw_id & TS_HW_ID_VARIANT_MASK) ? "TB" : "PCIe" ); } else { diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 699f764..26c32ac 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -727,7 +727,7 @@ int main(int argc, char** argv) if(hw_info & TS_HW_ID_VALID_MASK) { printf("HW Rev %02d - %s\n", hw_info & TS_HW_ID_REV_MASK, - (hw_info & TS_HW_ID_VARIANT_MASK) ? "PCIe" : "TB" ); + (hw_info & TS_HW_ID_VARIANT_MASK) ? "TB" : "PCIe" ); } else { diff --git a/src/spiflash.c b/src/spiflash.c index 37b39a4..5f5a87f 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -339,6 +339,7 @@ int32_t spiflash_init(file_t fd, spiflash_dev_t* dev) flash_id = spiflash_read_id_register(dev->fd); if((flash_id == 0x010219) || + (flash_id == 0xC22017) || (flash_id == 0xC22537) || (flash_id == 0xC22B27)) { @@ -362,6 +363,7 @@ int32_t spiflash_init(file_t fd, spiflash_dev_t* dev) dev->ops = s25fl256s_ops; break; } + case 0xC22017: case 0xC22537: case 0xC22B27: // Test SPI device MX25S6433F { From a0d198b4ff60aa318a416f7fc89ef01a1c8bcac1 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 21 May 2025 21:58:23 -0400 Subject: [PATCH 15/83] Fix atomic support in msvc. Make the ADC test mode a top-level api for testing. --- CMakeLists.txt | 5 ++++- example/thunderscope_test.cpp | 2 +- include/ts_calibration.h | 20 ++++++++++++++++++++ src/thunderscope.c | 13 +++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53781f8..3d6b5a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.22) project(libtslitex) @@ -7,6 +7,9 @@ set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD_REQUIRED ON) # error if compiler doesn't support c17 set(CMAKE_C_EXTENSIONS ON) +if(MSVC) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /std:c++17 /experimental:c11atomics") +endif(MSVC) enable_language(CXX) set(CMAKE_CXX_STANDARD 17) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 26c32ac..71bb170 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -348,7 +348,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ } // Uncomment to use Test Pattern - // ts_channel_set_adc_test(channels, HMCAD15_TEST_SYNC, 0, 0); + thunderscopeCalibrationAdcTest(tsHdl, TS_ADC_TEST_RAMP, 0); printf("- Checking HMCAD1520 Sample Rate..."); litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); diff --git a/include/ts_calibration.h b/include/ts_calibration.h index 93237cf..a1c83a8 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -17,6 +17,16 @@ extern "C" { #include #include "ts_common.h" +typedef enum tsCalAdcTest_e +{ + TS_ADC_TEST_DISABLE, + TS_ADC_TEST_SINGLE, + TS_ADC_TEST_DUAL, + TS_ADC_TEST_RAMP, + TS_ADC_TEST_DESKEW, + TS_ADC_TEST_SYNC +} tsCalAdcTest_t; + typedef struct tsChannelCalibration_s { int32_t buffer_mV; @@ -88,6 +98,16 @@ int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCal * @return int32_t TS_STATUS_OK if the parameters were applied */ int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl); + +/** + * @brief Manually set parameters for the devices in a channel to aid in calibration. + * + * @param ts Handle to the Thunderscope device + * @param test_mode Test Pattern (see HMCAD15xx Documentation) + * @param test_pattern Value used for Single/Dual test modes + * @return int32_t TS_STATUS_OK if the parameters were applied + */ +int32_t thunderscopeCalibrationAdcTest(tsHandle_t ts, tsCalAdcTest_t test_mode, uint32_t test_pattern); #ifdef __cplusplus } #endif diff --git a/src/thunderscope.c b/src/thunderscope.c index caa1ada..23a41d5 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -262,6 +262,19 @@ int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsCha return TS_STATUS_ERROR; } +int32_t thunderscopeCalibrationAdcTest(tsHandle_t ts, tsCalAdcTest_t test_mode, uint32_t test_pattern) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + return ts_channel_set_adc_test(pInst->pChannel, (hmcad15xxTestMode_t)test_mode, + (uint16_t)(test_pattern & 0xFFFF), (uint16_t)(test_pattern >> 16)); + } + + return TS_STATUS_ERROR; +} + int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable) { int32_t status = TS_STATUS_OK; From 3c7e8973ccc53c8cfa23d4fd9e3d292a24766841 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 21 May 2025 23:46:32 -0400 Subject: [PATCH 16/83] Add debug print for bitslip count --- example/thunderscope_test.cpp | 43 ++++++++++++++++------------------- include/ts_calibration.h | 2 +- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 71bb170..d9bbc31 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -306,21 +306,11 @@ static void test_io(file_t fd, bool isBeta) } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, - uint32_t volt_scale_mV, int32_t offset_mV, uint8_t ac_couple, uint8_t term) + uint32_t volt_scale_mV, int32_t offset_mV, uint8_t ac_couple, uint8_t term, bool watch_bitslip) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); - - // tsChannelHdl_t channels; - // ts_channel_init(&channels, fd); - // if(channels == NULL) - // { - // printf("Failed to create channels handle"); - // return; - // } - - // sampleStream_t samp; - // samples_init(&samp, 0, 0); + uint32_t bitslip_count = 0; uint8_t* sampleBuffer = (uint8_t*)calloc(TS_SAMPLE_BUFFER_SIZE, 0x1000); uint64_t sampleLen = 0; @@ -339,7 +329,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ chConfig.coupling = ac_couple ? TS_COUPLE_AC : TS_COUPLE_DC; chConfig.term = term ? TS_TERM_50 : TS_TERM_1M; chConfig.active = 1; - // ts_channel_params_set(channels, channel, &chConfig); thunderscopeChannelConfigSet(tsHdl, channel, &chConfig); numChan++; } @@ -355,6 +344,11 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ NS_DELAY(500000000); uint32_t rate = litepcie_readl(fd, CSR_ADC_HMCAD1520_SAMPLE_COUNT_ADDR) * 2; printf(" %d Samples/S\r\n", rate); + if(watch_bitslip) + { + bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); + printf("Bitslip Snapshot: %lu\r\n", bitslip_count); + } //Only start taking samples if the rate is non-zero @@ -362,8 +356,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { uint64_t data_sum = 0; //Start Sample capture - // samples_enable_set(&samp, 1); - // ts_channel_run(channels, 1); thunderscopeDataEnable(tsHdl, 1); auto startTime = std::chrono::steady_clock::now(); @@ -373,7 +365,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { uint32_t readReq = (TS_SAMPLE_BUFFER_SIZE * 0x100); //Collect Samples - // int32_t readRes = samples_get_buffers(&samp, sampleBuffer, readReq); int32_t readRes = thunderscopeRead(tsHdl, sampleBuffer, readReq); if(readRes < 0) { @@ -385,13 +376,17 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ } data_sum += readReq; sampleLen = readRes; + + if(watch_bitslip) + { + bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); + printf("Bitslip Snapshot: %lu\r\n", bitslip_count); + } } } auto endTime = std::chrono::steady_clock::now(); //Stop Samples - // samples_enable_set(&samp, 0); - // ts_channel_run(channels, 0); thunderscopeDataEnable(tsHdl, 0); auto deltaNs = std::chrono::duration_cast(endTime - startTime); @@ -402,15 +397,11 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ //Disable channels for(uint8_t i=0; i < TS_NUM_CHANNELS; i++) { - // ts_channel_params_get(channels, i, &chConfig); thunderscopeChannelConfigGet(tsHdl, i, &chConfig); chConfig.active = 0; - // ts_channel_params_set(channels, i, &chConfig); thunderscopeChannelConfigSet(tsHdl, i, &chConfig); } - // ts_channel_destroy(channels); - // samples_teardown(&samp); thunderscopeClose(tsHdl); if(sampleLen > 0) @@ -599,6 +590,7 @@ int main(int argc, char** argv) int32_t offset_mV = 0; uint8_t ac_couple = 0; uint8_t term = 0; + bool bitslip = false; struct optparse_long argList[] = { {"dev", 'd', OPTPARSE_REQUIRED}, @@ -608,6 +600,7 @@ int main(int argc, char** argv) {"offsetmv", 'o', OPTPARSE_REQUIRED}, {"ac", 'a', OPTPARSE_NONE}, {"term", 't', OPTPARSE_NONE}, + {"bits", 's', OPTPARSE_NONE}, {0} }; @@ -649,6 +642,10 @@ int main(int argc, char** argv) term = 1; argCount++; break; + case 's': + bitslip = true; + argCount++; + break; case '?': fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); print_help(); @@ -757,7 +754,7 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_mV, offset_mV, ac_couple, term); + test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_mV, offset_mV, ac_couple, term, bitslip); } // Flash test else if(0 == strcmp(arg, "flash")) diff --git a/include/ts_calibration.h b/include/ts_calibration.h index a1c83a8..496628d 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -100,7 +100,7 @@ int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCal int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl); /** - * @brief Manually set parameters for the devices in a channel to aid in calibration. + * @brief Manually set the ADC test pattern mode. * * @param ts Handle to the Thunderscope device * @param test_mode Test Pattern (see HMCAD15xx Documentation) From c73ac3a96af1774d09d992e2af5e47bca72d7b8a Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 22 May 2025 00:13:13 -0400 Subject: [PATCH 17/83] Disable data ramp in capture test --- example/thunderscope_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index d9bbc31..131e8d8 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -337,7 +337,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ } // Uncomment to use Test Pattern - thunderscopeCalibrationAdcTest(tsHdl, TS_ADC_TEST_RAMP, 0); + // thunderscopeCalibrationAdcTest(tsHdl, TS_ADC_TEST_RAMP, 0); printf("- Checking HMCAD1520 Sample Rate..."); litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); From e7343fee22ea6d89e5808967592e395072bb17c9 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Mon, 26 May 2025 21:57:27 -0400 Subject: [PATCH 18/83] Change calibration and offset voltages to microvolts. Update AFE to use full range of MCP4452 on Gamma devices --- bindings/python/CMakeLists.txt | 2 +- bindings/python/ts_calibration.pxd | 8 ++-- bindings/python/tslitex.pxd | 4 +- bindings/python/tslitex.pyx | 8 ++-- example/thunderscope_test.cpp | 24 ++++++------ include/ts_calibration.h | 8 ++-- include/ts_common.h | 4 +- src/afe.c | 63 +++++++++++++++++------------- src/afe.h | 7 ++-- src/mcp443x.h | 27 +++++++++---- src/platform.h | 10 +++-- src/ts_channel.c | 35 ++++++++--------- 12 files changed, 110 insertions(+), 90 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 5d2849c..c8b9a34 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -13,7 +13,7 @@ add_custom_command( DEPENDS tslitex.pyx tslitex.pxd VERBATIM ) - + set(PY_BIND_LIBS tslitex_static litepcie) if(WIN32) list(APPEND PY_BIND_LIBS setupapi) diff --git a/bindings/python/ts_calibration.pxd b/bindings/python/ts_calibration.pxd index b0373a3..941501c 100644 --- a/bindings/python/ts_calibration.pxd +++ b/bindings/python/ts_calibration.pxd @@ -13,8 +13,8 @@ cdef extern from "ts_calibration.h": ctypedef void* tsHandle_t cdef struct tsChannelCalibration_s: - int32_t buffer_mV - int32_t bias_mV + int32_t buffer_uV + int32_t bias_uV int32_t attenuatorGain1M_mdB int32_t attenuatorGain50_mdB int32_t bufferGain_mdB @@ -23,8 +23,8 @@ cdef extern from "ts_calibration.h": int32_t preampHighGainError_mdB int32_t preampAttenuatorGain_mdB[11] int32_t preampOutputGainError_mdB - int32_t preampLowOffset_mV - int32_t preampHighOffset_mV + int32_t preampLowOffset_uV + int32_t preampHighOffset_uV int32_t preampInputBias_uA ctypedef tsChannelCalibration_s tsChannelCalibration_t diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index c328dc5..647f715 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -42,8 +42,8 @@ cdef extern from "thunderscope.h": ctypedef tsDeviceInfo_s tsDeviceInfo_t cdef struct tsChannelParam_s: - uint32_t volt_scale_mV - int32_t volt_offset_mV + uint32_t volt_scale_uV + int32_t volt_offset_uV uint32_t bandwidth uint8_t coupling uint8_t term diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index a2e4763..715a0ad 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -57,12 +57,12 @@ cdef class Channel: cdef int32_t retVal = tslitex.thunderscopeChannelConfigGet(self.dev, self._channel, &self._params) if retVal != tslitex.TS_STATUS_OK: raise ValueError(f"Failed to retrieve Channel {self._channel} parameters") - return self._params.volt_scale_mV / 1000.0 + return self._params.volt_scale_uV / 1000000.0 @VoltScale.setter def VoltScale(self, volts: float): - self._params.volt_scale_mV = (volts*1000) + self._params.volt_scale_uV = (volts*1000000) cdef int32_t retVal retVal = tslitex.thunderscopeChannelConfigSet(self.dev, self._channel, &self._params) if retVal != tslitex.TS_STATUS_OK: @@ -73,12 +73,12 @@ cdef class Channel: cdef int32_t retVal = tslitex.thunderscopeChannelConfigGet(self.dev, self._channel, &self._params) if retVal != tslitex.TS_STATUS_OK: raise ValueError(f"Failed to retrieve Channel {self._channel} parameters") - return self._params.volt_offset_mV / 1000.0 + return self._params.volt_offset_uV / 1000000.0 @VoltOffset.setter def VoltOffset(self, volts: float): - self._params.volt_offset_mV = (volts*1000) + self._params.volt_offset_uV = (volts*1000000) cdef int32_t retVal retVal = tslitex.thunderscopeChannelConfigSet(self.dev, self._channel, &self._params) if retVal != tslitex.TS_STATUS_OK: diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 131e8d8..6e207be 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -306,7 +306,7 @@ static void test_io(file_t fd, bool isBeta) } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, - uint32_t volt_scale_mV, int32_t offset_mV, uint8_t ac_couple, uint8_t term, bool watch_bitslip) + uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); @@ -323,8 +323,8 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ if(channelBitmap & 0x1) { thunderscopeChannelConfigGet(tsHdl, channel, &chConfig); - chConfig.volt_scale_mV = volt_scale_mV; - chConfig.volt_offset_mV = offset_mV; + chConfig.volt_scale_uV = volt_scale_uV; + chConfig.volt_offset_uV = offset_uV; chConfig.bandwidth = bandwidth; chConfig.coupling = ac_couple ? TS_COUPLE_AC : TS_COUPLE_DC; chConfig.term = term ? TS_TERM_50 : TS_TERM_1M; @@ -565,8 +565,8 @@ static void print_help(void) printf("\t\t -d Device Index\r\n"); printf("\t\t -c Channel bitmap\r\n"); printf("\t\t -b Channel Bandwidth [MHz]\r\n"); - printf("\t\t -v Channel Full Scale Volts [millivolt]\r\n"); - printf("\t\t -o Channel Offset [millivolt]\r\n"); + printf("\t\t -v Channel Full Scale Volts [microvolt]\r\n"); + printf("\t\t -o Channel Offset [microvolt]\r\n"); printf("\t\t -a AC Couple\r\n"); printf("\t\t -t 50 Ohm termination\r\n"); } @@ -586,8 +586,8 @@ int main(int argc, char** argv) int i; uint8_t channelBitmap = 0x0F; uint16_t bandwidth = 350; - uint32_t volt_scale_mV = 10000; - int32_t offset_mV = 0; + uint32_t volt_scale_uV = 10000000; + int32_t offset_uV = 0; uint8_t ac_couple = 0; uint8_t term = 0; bool bitslip = false; @@ -596,8 +596,8 @@ int main(int argc, char** argv) {"dev", 'd', OPTPARSE_REQUIRED}, {"chan", 'c', OPTPARSE_REQUIRED}, {"bw", 'b', OPTPARSE_REQUIRED}, - {"voltsmv", 'v', OPTPARSE_REQUIRED}, - {"offsetmv", 'o', OPTPARSE_REQUIRED}, + {"voltsuv", 'v', OPTPARSE_REQUIRED}, + {"offsetuv", 'o', OPTPARSE_REQUIRED}, {"ac", 'a', OPTPARSE_NONE}, {"term", 't', OPTPARSE_NONE}, {"bits", 's', OPTPARSE_NONE}, @@ -627,11 +627,11 @@ int main(int argc, char** argv) argCount+=2; break; case 'v': - volt_scale_mV = strtol(options.optarg, NULL, 0); + volt_scale_uV = strtol(options.optarg, NULL, 0); argCount+=2; break; case 'o': - offset_mV = strtol(options.optarg, NULL, 0); + offset_uV = strtol(options.optarg, NULL, 0); argCount+=2; break; case 'a': @@ -754,7 +754,7 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_mV, offset_mV, ac_couple, term, bitslip); + test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip); } // Flash test else if(0 == strcmp(arg, "flash")) diff --git a/include/ts_calibration.h b/include/ts_calibration.h index 496628d..a0d0ade 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -29,8 +29,8 @@ typedef enum tsCalAdcTest_e typedef struct tsChannelCalibration_s { - int32_t buffer_mV; - int32_t bias_mV; + int32_t buffer_uV; + int32_t bias_uV; int32_t attenuatorGain1M_mdB; int32_t attenuatorGain50_mdB; int32_t bufferGain_mdB; @@ -39,8 +39,8 @@ typedef struct tsChannelCalibration_s int32_t preampHighGainError_mdB; int32_t preampAttenuatorGain_mdB[11]; int32_t preampOutputGainError_mdB; - int32_t preampLowOffset_mV; - int32_t preampHighOffset_mV; + int32_t preampLowOffset_uV; + int32_t preampHighOffset_uV; int32_t preampInputBias_uA; } tsChannelCalibration_t; diff --git a/include/ts_common.h b/include/ts_common.h index 92a92fc..0b824f2 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -64,8 +64,8 @@ typedef struct tsDeviceInfo_s typedef struct tsChannelParam_s { - uint32_t volt_scale_mV; /**< Set full scale voltage in millivolts */ - int32_t volt_offset_mV; /**< Set offset voltage in millivolts */ + uint32_t volt_scale_uV; /**< Set full scale voltage in microvolts */ + int32_t volt_offset_uV; /**< Set offset voltage in microvolts */ uint32_t bandwidth; /**< Set Bandwidth Filter in MHz. Next highest filter will be selected */ uint8_t coupling; /**< Select AD/DC coupling for channel. Use tsChannelCoupling_t enum */ uint8_t term; /**< Select Termination mode for channel. Use tsChannelTerm_t enum */ diff --git a/src/afe.c b/src/afe.c index 40c2882..0d13544 100644 --- a/src/afe.c +++ b/src/afe.c @@ -44,6 +44,14 @@ int32_t ts_afe_init(ts_afe_t* afe, uint8_t channel, spi_dev_t afe_amp, i2c_t tri afe->trimDacCh = dacCh; afe->trimPot = trimPot; afe->trimPotCh = potCh; + if(isBetaDevice(trimDac.fd)) + { + afe->trimPotBits = MCP4432_NUM_BITS; + } + else + { + afe->trimPotBits = MCP4452_NUM_BITS; + } afe->termPin = termination; afe->attenuatorPin = attenuator; afe->couplingPin = coupling; @@ -54,17 +62,17 @@ int32_t ts_afe_init(ts_afe_t* afe, uint8_t channel, spi_dev_t afe_amp, i2c_t tri afe->isAttenuated = true; // Default calibration - afe->cal.buffer_mV = TS_VBUFFER_NOMINAL_MV; - afe->cal.bias_mV = TS_VBIAS_NOMINAL_MV; + afe->cal.buffer_uV = TS_VBUFFER_NOMINAL_UV; + afe->cal.bias_uV = TS_VBIAS_NOMINAL_UV; afe->cal.attenuatorGain1M_mdB = TS_ATTENUATION_1M_GAIN_mdB; afe->cal.attenuatorGain50_mdB = TS_TERMINATION_50OHM_GAIN_mdB; afe->cal.bufferGain_mdB = TS_BUFFER_GAIN_NOMINAL_mdB; - afe->cal.trimRheostat_range = MCP4432_FULL_SCALE_OHM; + afe->cal.trimRheostat_range = MCP4452_104_FULL_SCALE_OHM; afe->cal.preampLowGainError_mdB = 0; afe->cal.preampHighGainError_mdB = 0; afe->cal.preampOutputGainError_mdB = 0; - afe->cal.preampLowOffset_mV = 0; - afe->cal.preampHighOffset_mV = 0; + afe->cal.preampLowOffset_uV = 0; + afe->cal.preampHighOffset_uV = 0; afe->cal.preampInputBias_uA = TS_PREAMP_INPUT_BIAS_CURRENT_uA; Mcp4728ChannelConfig_t trimConf = {0}; @@ -152,14 +160,14 @@ int32_t ts_afe_set_gain(ts_afe_t* afe, int32_t gain_mdB) return gain_actual; } -int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_mV, int32_t* offset_actual) +int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_uV, int32_t* offset_actual) { uint16_t offsetVal = TS_TRIM_DAC_DEFAULT; - int32_t V_dac = 0; - int32_t R_trim = 0; - int32_t gain_afe = 0; - int32_t V_zero = 0; - int32_t gain_preamp = 0; + int64_t V_dac = 0; + int64_t R_trim = 0; + int64_t gain_afe = 0; + int64_t V_zero = 0; + int64_t gain_preamp = 0; if(NULL == afe || NULL == offset_actual) { @@ -177,31 +185,30 @@ int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_mV, int32_t* offset_actu gain_afe += afe->cal.attenuatorGain1M_mdB; } - V_zero = afe->cal.buffer_mV; + V_zero = afe->cal.buffer_uV; gain_preamp = lmh6518_gain_from_config(afe->ampConf); if(afe->ampConf.preamp == PREAMP_LG) { gain_preamp += afe->cal.preampLowGainError_mdB; - V_zero += (int32_t)((double)afe->cal.preampLowOffset_mV / pow(10, ((double)gain_preamp/20000.0))); + V_zero += (int32_t)((double)afe->cal.preampLowOffset_uV / pow(10, ((double)gain_preamp/20000.0))); } else { gain_preamp += afe->cal.preampHighGainError_mdB; - V_zero += (int32_t)((double)afe->cal.preampHighOffset_mV / pow(10, ((double)gain_preamp/20000.0))); + V_zero += (int32_t)((double)afe->cal.preampHighOffset_uV / pow(10, ((double)gain_preamp/20000.0))); } // Desired Trim Voltage - LOG_DEBUG("AFE Offset Request %d mv with %d mdB Input Gain", offset_mV, gain_afe); - int32_t V_trim = V_zero + (uint32_t)((double)offset_mV * pow(10.0, (double)gain_afe/20000.0)); - LOG_DEBUG("AFE Offset target V_trim %d mV compared to V_zero of %d mV", V_trim, V_zero); + LOG_DEBUG("AFE Offset Request %d uv with %lld mdB Input Gain", offset_uV, gain_afe); + int64_t V_trim = V_zero + (int64_t)((double)offset_uV * pow(10.0, (double)gain_afe/20000.0)); + LOG_DEBUG("AFE Offset target V_trim %lld uV compared to V_zero of %lld uV", V_trim, V_zero); // Progressively reduce R_trim until V_dac is within range of 0-VDD - uint8_t trimPotVal = MCP4432_MAX; + uint8_t trimPotVal = (1 << afe->trimPotBits); do { - // R_trim = MCP4432_503_OHM(trimPotVal); - R_trim = ((((trimPotVal) * afe->cal.trimRheostat_range) / MCP4432_MAX) + MCP4432_RWIPER); + R_trim = ((((trimPotVal) * afe->cal.trimRheostat_range) / ((1 << afe->trimPotBits))) + MCP4432_RWIPER); /** * Preamp sinks an input bias current (I_trim), so need to add this factor when comparing current @@ -212,18 +219,18 @@ int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_mV, int32_t* offset_actu * Solved for V_dac becomes: * V_dac = V_trim + R_trim * ((V_trim - V_bias)/R_bias + I_trim) */ - V_dac = V_trim + (R_trim * ((1000 * (V_trim - afe->cal.bias_mV)/TS_BIAS_RESISTOR_NOMINAL) + afe->cal.preampInputBias_uA)) / 1000; - if(V_dac > 0 && V_dac < TS_AFE_TRIM_VDD_NOMINAL) + V_dac = V_trim + (R_trim * (((V_trim - (int64_t)afe->cal.bias_uV)/TS_BIAS_RESISTOR_NOMINAL) + afe->cal.preampInputBias_uA)); + if(V_dac >= 0 && V_dac <= TS_AFE_TRIM_VDD_NOMINAL) { - LOG_DEBUG("Setting Vdac to %d mV, Rtrim to %d Ohm", V_dac, R_trim); + LOG_DEBUG("Setting Vdac to %lld uV, Rtrim to %lld Ohm", V_dac, R_trim); offsetVal = (V_dac * MCP4728_FULL_SCALE_VAL) / TS_AFE_TRIM_VDD_NOMINAL; break; } if(trimPotVal == 0) { - LOG_ERROR("AFE Unable to produce Trim voltage %d for requested offset %d", V_trim, offset_mV); - if(V_trim > afe->cal.bias_mV) + LOG_ERROR("AFE Unable to produce Trim voltage %lld for requested offset %d", V_trim, offset_uV); + if(V_trim > afe->cal.bias_uV) { V_dac = TS_AFE_TRIM_VDD_NOMINAL; offsetVal = MCP4728_FULL_SCALE_VAL; @@ -254,11 +261,11 @@ int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_mV, int32_t* offset_actu // Reverse offset calc V_dac = (offsetVal * TS_AFE_TRIM_VDD_NOMINAL) / MCP4728_FULL_SCALE_VAL; - V_trim = (int32_t) ((((double)TS_BIAS_RESISTOR_NOMINAL * (double)V_dac) + ((double)afe->cal.bias_mV * R_trim) - - ((double)TS_BIAS_RESISTOR_NOMINAL * (double)R_trim * (double)afe->cal.preampInputBias_uA/1000.0)) + V_trim = (int64_t) ((((double)TS_BIAS_RESISTOR_NOMINAL * (double)V_dac) + ((double)afe->cal.bias_uV * R_trim) + - ((double)TS_BIAS_RESISTOR_NOMINAL * (double)R_trim * (double)afe->cal.preampInputBias_uA)) /(TS_BIAS_RESISTOR_NOMINAL + R_trim)); *offset_actual = (int32_t)(((double)V_trim - ((double)V_zero)) / pow(10.0, (double)gain_afe/20000.0)); - LOG_DEBUG("AFE Offset actual V_trim %d mv, Offset %d mV", V_trim, *offset_actual); + LOG_DEBUG("AFE Offset actual V_trim %lld uv, Offset %d uV", V_trim, *offset_actual); return TS_STATUS_OK; } diff --git a/src/afe.h b/src/afe.h index 9ed4eb3..5f32ab4 100644 --- a/src/afe.h +++ b/src/afe.h @@ -31,6 +31,7 @@ typedef struct ts_afe_s uint8_t trimDacCh; i2c_t trimPot; uint8_t trimPotCh; + uint8_t trimPotBits; gpio_t termPin; tsChannelTerm_t termination; gpio_t attenuatorPin; @@ -72,11 +73,11 @@ int32_t ts_afe_set_gain(ts_afe_t* afe, int32_t gain_mdB); * @brief Set the AFE input gain for a channel * * @param afe Pointer to an AFE instance - * @param offset_mV Offset in mV - * @param offset_mV Applied Offset in mV + * @param offset_uV Offset in uV + * @param offset_uV Applied Offset in uV * @return int32_t TS_STATUS_OK on success, else error */ -int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_mV, int32_t* offset_actual); +int32_t ts_afe_set_offset(ts_afe_t* afe, int32_t offset_uV, int32_t* offset_actual); /** * @brief Set the AFE Bandwidth filter for a channel diff --git a/src/mcp443x.h b/src/mcp443x.h index 1b7be58..7743165 100644 --- a/src/mcp443x.h +++ b/src/mcp443x.h @@ -21,14 +21,25 @@ extern "C" { #define MCP4432_NUM_CH (4) -#define MCP4432_RWIPER (75) -#define MCP4432_MAX (1 << 7) -#define MCP4432_MIN (0) -#define MCP4432_DEFAULT (0x40) -#define MCP4432_FULL_SCALE_OHM (50000) - -#define MCP4432_503_SET(x) ((((x) - MCP4432_RWIPER) * MCP4432_MAX) / MCP4432_FULL_SCALE_OHM) -#define MCP4432_503_OHM(x) ((((x) * MCP4432_FULL_SCALE_OHM) / MCP4432_MAX) + MCP4432_RWIPER) +#define MCP4432_RWIPER (75) +#define MCP4432_NUM_BITS (7) +#define MCP4432_MAX (1 << 7) +#define MCP4432_MIN (0) +#define MCP4432_DEFAULT (0x40) + +#define MCP4432_503_FULL_SCALE_OHM (50000) +#define MCP4432_503_SET(x) ((((x) - MCP4432_RWIPER) * MCP4432_MAX) / MCP4432_503_FULL_SCALE_OHM) +#define MCP4432_503_OHM(x) ((((x) * MCP4432_503_FULL_SCALE_OHM) / MCP4432_MAX) + MCP4432_RWIPER) + +#define MCP4452_RWIPER (75) +#define MCP4452_NUM_BITS (8) +#define MCP4452_MAX (1 << 8) +#define MCP4452_MIN (0) +#define MCP4452_DEFAULT (0x40) + +#define MCP4452_104_FULL_SCALE_OHM (100000) +#define MCP4452_104_SET(x) ((((x) - MCP4452_RWIPER) * MCP4452_MAX) / MCP4452_104_FULL_SCALE_OHM) +#define MCP4452_104_OHM(x) ((((x) * MCP4452_104_FULL_SCALE_OHM) / MCP4452_MAX) + MCP4452_RWIPER) int32_t mcp443x_set_wiper(i2c_t dev, uint8_t channel, uint8_t val); diff --git a/src/platform.h b/src/platform.h index 0f98060..fbb1c31 100644 --- a/src/platform.h +++ b/src/platform.h @@ -29,13 +29,13 @@ extern "C" { #define TS_ADC_CH_NO_INVERT (0) #define TS_ADC_CH_INVERT (1) -#define TS_AFE_OUTPUT_NOMINAL_mVPP (700.0) +#define TS_AFE_OUTPUT_NOMINAL_uVPP (700000.0) #define TS_ATTENUATION_1M_GAIN_mdB (-33979) /**< 50x Attenuation = 20 * log(1/50) * 1000 */ #define TS_TERMINATION_50OHM_GAIN_mdB (-13979) /**< 5x Attenuation from 50Ohm mode. 20 * log(1/5) * 1000 */ -#define TS_VBUFFER_NOMINAL_MV (2500) -#define TS_VBIAS_NOMINAL_MV (2500) -#define TS_AFE_TRIM_VDD_NOMINAL (5000) +#define TS_VBUFFER_NOMINAL_UV (2500000) +#define TS_VBIAS_NOMINAL_UV (2500000) +#define TS_AFE_TRIM_VDD_NOMINAL (5000000) #define TS_BUFFER_GAIN_NOMINAL_mdB (-250) #define TS_BIAS_RESISTOR_NOMINAL (500) #define TS_PREAMP_INPUT_BIAS_CURRENT_uA (40) @@ -186,6 +186,8 @@ extern const flash_layout_t ts_64Mb_layout; #define TS_FLASH_64M_MFG (0xC2) //Macronix #define TS_FLASH_64M_ID (0x2537) //64Mb SPI Flash +inline bool isBetaDevice(file_t ts) { return (0 == (litepcie_readl(ts, CSR_DEV_STATUS_HW_ID_ADDR) & (1 << CSR_DEV_STATUS_HW_ID_HW_VALID_OFFSET)));} + #ifdef __cplusplus } #endif diff --git a/src/ts_channel.c b/src/ts_channel.c index 5221bcf..848e0e5 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -110,8 +110,8 @@ const static tsChannelParam_t g_tsParamsDefault = {.active = false, .bandwidth = 0, .coupling = TS_COUPLE_DC, .term = TS_TERM_1M, - .volt_offset_mV = 0, - .volt_scale_mV = 700}; + .volt_offset_uV = 0, + .volt_scale_uV = 700000}; static int32_t ts_channel_update_params(ts_channel_t* pTsHdl, uint32_t chanIdx, tsChannelParam_t* param, bool force); static int32_t ts_channel_health_update(ts_channel_t* pTsHdl); @@ -429,46 +429,45 @@ static int32_t ts_channel_update_params(ts_channel_t* pTsHdl, uint32_t chanIdx, } //Set Voltage Scale - if(needUpdateGain || (param->volt_scale_mV != pTsHdl->chan[chanIdx].params.volt_scale_mV) || force) + if(needUpdateGain || (param->volt_scale_uV != pTsHdl->chan[chanIdx].params.volt_scale_uV) || force) { //Calculate dB gain value //TODO: Set both AFE and ADC gain? - int32_t afe_gain_mdB = (int32_t)(20000 * log10(TS_AFE_OUTPUT_NOMINAL_mVPP / (double)param->volt_scale_mV)); + int32_t afe_gain_mdB = (int32_t)(20000 * log10(TS_AFE_OUTPUT_NOMINAL_uVPP / (double)param->volt_scale_uV)); LOG_DEBUG("Channel %d AFE request %i mdB gain", chanIdx, afe_gain_mdB); retVal = ts_afe_set_gain(&pTsHdl->chan[chanIdx].afe, afe_gain_mdB); if(TS_STATUS_ERROR == retVal) { - LOG_ERROR("Unable to set Channel %d voltage scale: %x", chanIdx, param->volt_scale_mV); + LOG_ERROR("Unable to set Channel %d voltage scale: %x", chanIdx, param->volt_scale_uV); return TS_INVALID_PARAM; } else { LOG_DEBUG("Channel %d AFE set to %i mdB gain", chanIdx, retVal); - retVal = (int32_t)(TS_AFE_OUTPUT_NOMINAL_mVPP / pow(10.0, (double)retVal / 20000.0)); - LOG_DEBUG("Channel %d voltage scale Request: %d Actual: %d", chanIdx, param->volt_scale_mV, retVal); - pTsHdl->chan[chanIdx].params.volt_scale_mV = retVal; + retVal = (int32_t)(TS_AFE_OUTPUT_NOMINAL_uVPP / pow(10.0, (double)retVal / 20000.0)); + LOG_DEBUG("Channel %d voltage scale Request: %d Actual: %d", chanIdx, param->volt_scale_uV, retVal); + pTsHdl->chan[chanIdx].params.volt_scale_uV = retVal; needUpdateOffset = true; } } //Set Voltage Offset - if(needUpdateOffset || (param->volt_offset_mV != pTsHdl->chan[chanIdx].params.volt_offset_mV) || force) + if(needUpdateOffset || (param->volt_offset_uV != pTsHdl->chan[chanIdx].params.volt_offset_uV) || force) { //Adjust Trim DAC int32_t offset_actual = 0; - retVal = ts_afe_set_offset(&pTsHdl->chan[chanIdx].afe, param->volt_offset_mV, &offset_actual); + retVal = ts_afe_set_offset(&pTsHdl->chan[chanIdx].afe, param->volt_offset_uV, &offset_actual); if(TS_STATUS_OK != retVal) { - LOG_ERROR("Unable to set Channel %d voltage offset: %i", chanIdx, param->volt_offset_mV); + LOG_ERROR("Unable to set Channel %d voltage offset: %i", chanIdx, param->volt_offset_uV); return TS_INVALID_PARAM; } else { - LOG_DEBUG("Channel %d AFE set to %i mV offset", chanIdx, offset_actual); - // pTsHdl->chan[chanIdx].params.volt_offset_mV = offset_actual; - pTsHdl->chan[chanIdx].params.volt_offset_mV = param->volt_offset_mV; + LOG_DEBUG("Channel %d AFE set to %i uV offset", chanIdx, offset_actual); + pTsHdl->chan[chanIdx].params.volt_offset_uV = param->volt_offset_uV; } } @@ -623,8 +622,8 @@ int32_t ts_channel_calibration_set(tsChannelHdl_t tsChannels, uint32_t chanIdx, ts->chan[chanIdx].afe.cal = *cal; LOG_DEBUG("Received Calibration for channel %d", chanIdx); - LOG_DEBUG("\tBuffer Output: %d mV", cal->buffer_mV); - LOG_DEBUG("\t+VBIAS: %d mV", cal->bias_mV); + LOG_DEBUG("\tBuffer Output: %d uV", cal->buffer_uV); + LOG_DEBUG("\t+VBIAS: %d uV", cal->bias_uV); LOG_DEBUG("\t1M Attenuator Gain: %d mdB", cal->attenuatorGain1M_mdB); LOG_DEBUG("\t50 Ohm Terminator Gain: %d mdB", cal->attenuatorGain50_mdB); LOG_DEBUG("\tBuffer Output Gain: %d mdB", cal->bufferGain_mdB); @@ -632,8 +631,8 @@ int32_t ts_channel_calibration_set(tsChannelHdl_t tsChannels, uint32_t chanIdx, LOG_DEBUG("\tPreamp Low Input Gain Error: %d mdB", cal->preampLowGainError_mdB); LOG_DEBUG("\tPreamp High Input Gain Error: %d mdB", cal->preampHighGainError_mdB); LOG_DEBUG("\tPreamp High Input Gain Error: %d mdB", cal->preampOutputGainError_mdB); - LOG_DEBUG("\tPreamp Low Output Offset: %d mV", cal->preampLowOffset_mV); - LOG_DEBUG("\tPreamp High Output Offset: %d mV", cal->preampHighOffset_mV); + LOG_DEBUG("\tPreamp Low Output Offset: %d uV", cal->preampLowOffset_uV); + LOG_DEBUG("\tPreamp High Output Offset: %d uV", cal->preampHighOffset_uV); LOG_DEBUG("\tPreamp Input Bias Current: %d uA", cal->preampInputBias_uA); //Force afe to recalculate gain/offsets From a354e66f8afc14a54f43e7aebf8ba67d9f89a96f Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 29 May 2025 01:39:08 -0400 Subject: [PATCH 19/83] Initial support for ADC branch calibration --- bindings/python/ts_calibration.pxd | 18 +++++++++++---- bindings/python/tslitex.pyx | 18 +++++++++++++-- example/thunderscope_test.cpp | 1 + include/ts_calibration.h | 34 ++++++++++++++++++++++++----- src/adc.c | 34 ++++++++++++++++++++++++++--- src/adc.h | 22 +++++++++++++++++-- src/hmcad15xx.c | 35 +++++++++++++++++++++++++++++- src/hmcad15xx.h | 16 +++++++++++++- src/thunderscope.c | 28 ++++++++++++++++++++++-- src/ts_channel.c | 32 ++++++++++++++++++++++++++- src/ts_channel.h | 18 +++++++++++++++ 11 files changed, 235 insertions(+), 21 deletions(-) diff --git a/bindings/python/ts_calibration.pxd b/bindings/python/ts_calibration.pxd index 941501c..2ce2fc0 100644 --- a/bindings/python/ts_calibration.pxd +++ b/bindings/python/ts_calibration.pxd @@ -29,7 +29,12 @@ cdef extern from "ts_calibration.h": ctypedef tsChannelCalibration_s tsChannelCalibration_t - cdef struct tsChannelCtrl_e: + cdef struct tsAdcCalibration_s: + uint8_t branchFineGain[8] + + ctypedef tsAdcCalibration_s tsAdcCalibration_t + + cdef struct tsChannelCtrl_s: uint8_t atten uint8_t term uint8_t dc_couple @@ -39,15 +44,20 @@ cdef extern from "ts_calibration.h": uint8_t pga_atten uint8_t pga_bw - ctypedef tsChannelCtrl_e tsChannelCtrl_t + ctypedef tsChannelCtrl_s tsChannelCtrl_t cdef struct tsScopeCalibration_s: tsChannelCalibration_t afeCal[4] + tsAdcCalibration_t adcCal ctypedef tsScopeCalibration_s tsScopeCalibration_t - int32_t thunderscopeCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t* cal) + int32_t thunderscopeChanCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t* cal) + + int32_t thunderscopeChanCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t* cal) + + int32_t thunderscopeAdcCalibrationSet(tsHandle_t ts, tsAdcCalibration_t* cal) - int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t* cal) + int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t* cal) int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl) diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index 715a0ad..1e7f916 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -144,14 +144,14 @@ cdef class Channel: def CalibrationSet(self, calibration: ts_calibration.tsChannelCalibration_t): cdef int32_t retVal - retVal = ts_calibration.thunderscopeCalibrationSet(self.dev, self._channel, &calibration) + retVal = ts_calibration.thunderscopeChanCalibrationSet(self.dev, self._channel, &calibration) if retVal != tslitex.TS_STATUS_OK: raise ValueError(f"Failed to set Channel {self._channel} Calibration {calibration}") def Calibration(self): cdef int32_t retVal cdef ts_calibration.tsChannelCalibration_t calibration - retVal = ts_calibration.thunderscopeCalibrationSet(self.dev, self._channel, &calibration) + retVal = ts_calibration.thunderscopeChanCalibrationSet(self.dev, self._channel, &calibration) if retVal != tslitex.TS_STATUS_OK: raise ValueError(f"Failed to get Channel {self._channel} Calibration ({retVal})") return calibration @@ -187,6 +187,20 @@ cdef class Thunderscope: tslitex.thunderscopeStatusGet(self._tsHandle, &status_vals) return status_vals + def AdcCalibrationSet(self, calibration: ts_calibration.tsAdcCalibration_t): + cdef int32_t retVal + retVal = ts_calibration.thunderscopeAdcCalibrationSet(self._tsHandle, &calibration) + if retVal != tslitex.TS_STATUS_OK: + raise ValueError(f"Failed to set ADC Calibration {calibration}") + + def AdcCalibration(self): + cdef int32_t retVal + cdef ts_calibration.tsAdcCalibration_t calibration + retVal = ts_calibration.thunderscopeAdcCalibrationGet(self._tsHandle, &calibration) + if retVal != tslitex.TS_STATUS_OK: + raise ValueError(f"Failed to get ADC Calibration ({retVal})") + return calibration + def firmwareUpdate(self, bitfile): if type(bitfile) is not bytes: raise TypeError(f"bitfile arg must be 'bytes' type") diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 6e207be..45f235b 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -472,6 +472,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ outWav.printSummary(); outWav.save(TS_TEST_WAV_FILE); } + free(sampleBuffer); } static void flash_test(char* arg, file_t fd) diff --git a/include/ts_calibration.h b/include/ts_calibration.h index a0d0ade..a66353a 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -44,7 +44,13 @@ typedef struct tsChannelCalibration_s int32_t preampInputBias_uA; } tsChannelCalibration_t; -typedef struct tsChannelCtrl_e +typedef struct tsAdcCalibration_s +{ + // Fine Gain Branch Adjustment + uint8_t branchFineGain[8]; +} tsAdcCalibration_t; + +typedef struct tsChannelCtrl_s { // FE Attenuator uint8_t atten; @@ -65,7 +71,7 @@ typedef struct tsChannelCtrl_e typedef struct tsScopeCalibration_s { tsChannelCalibration_t afeCal[TS_NUM_CHANNELS]; - + tsAdcCalibration_t adcCal; } tsScopeCalibration_t; @@ -74,10 +80,10 @@ typedef struct tsScopeCalibration_s * * @param ts Handle to the Thunderscope device * @param channel Channel number - * @param cal TBD Calibration data + * @param cal Channel Calibration data * @return int32_t TS_STATUS_OK if the calibration was accepted */ -int32_t thunderscopeCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal); +int32_t thunderscopeChanCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal); /** * @brief Get the calibration data for a channel on the Thunderscope device @@ -87,7 +93,25 @@ int32_t thunderscopeCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCal * @param cal Calibration Data Pointer * @return int32_t TS_STATUS_OK if the calibration was retrieved */ -int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal); +int32_t thunderscopeChanCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal); + +/** + * @brief Set the calibration data for the ADC on the Thunderscope device + * + * @param ts Handle to the Thunderscope device + * @param cal ADC Calibration data + * @return int32_t TS_STATUS_OK if the calibration was accepted + */ +int32_t thunderscopeAdcCalibrationSet(tsHandle_t ts, tsAdcCalibration_t *cal); + +/** + * @brief Get the calibration data for the ADC on the Thunderscope device + * + * @param ts Handle to the Thunderscope device + * @param cal ADC Calibration data Pointer + * @return int32_t TS_STATUS_OK if the calibration was retrieved + */ +int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t *cal); /** * @brief Manually set parameters for the devices in a channel to aid in calibration. diff --git a/src/adc.c b/src/adc.c index e7fffbe..9c7b1a7 100644 --- a/src/adc.c +++ b/src/adc.c @@ -13,6 +13,7 @@ #include "platform.h" #include "util.h" #include "liblitepcie.h" +#include "ts_calibration.h" typedef enum adc_shuffle_e @@ -71,7 +72,7 @@ int32_t ts_adc_set_channel_conf(ts_adc_t* adc, uint8_t channel, uint8_t input, u return retVal; } -int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse, int32_t gainFine) +int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse) { int32_t retVal = TS_STATUS_OK; @@ -82,14 +83,12 @@ int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse, int3 else { adc->tsChannels[channel].coarse = gainCoarse; - adc->tsChannels[channel].fine = gainFine; for(uint8_t i = 0; i < TS_NUM_CHANNELS; i++) { if(adc->tsChannels[channel].input == adc->adcDev.channelCfg[i].input) { adc->adcDev.channelCfg[i].coarse = gainCoarse; - adc->adcDev.channelCfg[i].fine = gainFine; break; } } @@ -219,3 +218,32 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res return TS_STATUS_ERROR; } } + +int32_t ts_adc_cal_set(ts_adc_t* adc, tsAdcCalibration_t *cal) +{ + if(!adc) + { + return TS_STATUS_ERROR; + } + + for(int i=0; i < HMCAD15_NUM_BRANCHES; i++) + { + adc->adcDev.fineCal[i] = cal->branchFineGain[i]; + } + + return hmcad15xx_fine_gain_set(&adc->adcDev, true); +} + +int32_t ts_adc_cal_get(ts_adc_t* adc, tsAdcCalibration_t *cal) +{ + if(!adc) + { + return TS_STATUS_ERROR; + } + + for(int i=0; i < HMCAD15_NUM_BRANCHES; i++) + { + cal->branchFineGain[i] = adc->adcDev.fineCal[i]; + } + return TS_STATUS_OK; +} \ No newline at end of file diff --git a/src/adc.h b/src/adc.h index e0ecc49..d0422d2 100644 --- a/src/adc.h +++ b/src/adc.h @@ -14,6 +14,7 @@ extern "C" { #endif #include "ts_common.h" +#include "ts_calibration.h" #include "hmcad15xx.h" #include "platform.h" #include "spi.h" @@ -53,10 +54,9 @@ int32_t ts_adc_set_channel_conf(ts_adc_t* adc, uint8_t channel, uint8_t input, u * @param adc Pointer to a ADC instance * @param channel Thunderscope Channel number to configure * @param gainCoarse Coarse Gain parameter for the ADC Input - * @param gainFine Fine Gain parameter for the ADC Input * @return int32_t TS_STATUS_OK if the gain was set successfully */ -int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse, int32_t gainFine); +int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse); /** * @brief Enable or Disable a Thunderscope Channel ADC @@ -95,6 +95,24 @@ int32_t ts_adc_run(ts_adc_t* adc, uint8_t en); */ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t resolution); +/** + * @brief Set the calibration on the ADC + * + * @param adc Pointer to a ADC instance + * @param cal Pointer to a ADC Calibration structure + * @return int32_t TS_STATUS_OK if the calibration was applied successfully + */ +int32_t ts_adc_cal_set(ts_adc_t* adc, tsAdcCalibration_t *cal); + +/** + * @brief Set the calibration on the ADC + * + * @param adc Pointer to a ADC instance + * @param cal Pointer to a ADC Calibration structure + * @return int32_t TS_STATUS_OK if the calibration was applied successfully + */ +int32_t ts_adc_cal_get(ts_adc_t* adc, tsAdcCalibration_t *cal); + #ifdef __cplusplus } #endif diff --git a/src/hmcad15xx.c b/src/hmcad15xx.c index c30ef92..37fb009 100644 --- a/src/hmcad15xx.c +++ b/src/hmcad15xx.c @@ -39,7 +39,6 @@ int32_t hmcad15xx_init(hmcad15xxADC_t* adc, spi_dev_t dev) adc->channelCfg[0].input = HMCAD15_ADC_IN1; adc->channelCfg[0].invert = 1; adc->channelCfg[0].coarse = 0; - adc->channelCfg[0].fine = 0; adc->clockDiv = HMCAD15_CLK_DIV_1; adc->fullScale_x10 = HMCAD15_FULL_SCALE_DEFAULT; adc->drive = HMCAD15_LVDS_DS_15; @@ -175,6 +174,40 @@ int32_t hmcad15xx_full_scale_adjust(hmcad15xxADC_t* adc, int8_t adjustment) return TS_STATUS_OK; } +int32_t hmcad15xx_fine_gain_set(hmcad15xxADC_t* adc, bool enable) +{ + uint16_t gainVals = 0; + if(!adc) + { + return TS_STATUS_ERROR; + } + + if(enable) + { + gainVals = adc->fineCal[0]; + gainVals |= adc->fineCal[1] << 8; + hmcad15xxRegWrite(adc, HMCAD15_REG_FINE_GAIN_1_2, gainVals); + gainVals = adc->fineCal[2]; + gainVals |= adc->fineCal[3] << 8; + hmcad15xxRegWrite(adc, HMCAD15_REG_FINE_GAIN_3_4, gainVals); + gainVals = adc->fineCal[4]; + gainVals |= adc->fineCal[5] << 8; + hmcad15xxRegWrite(adc, HMCAD15_REG_FINE_GAIN_5_6, gainVals); + gainVals = adc->fineCal[6]; + gainVals |= adc->fineCal[7] << 8; + hmcad15xxRegWrite(adc, HMCAD15_REG_FINE_GAIN_7_8, gainVals); + //Enable Fine Gain Control + hmcad15xxRegWrite(adc, HMCAD15_REG_GAIN_SEL, HMCAD15_FGAIN_EN); + } + else + { + //Disable Fine Gain Control + hmcad15xxRegWrite(adc, HMCAD15_REG_GAIN_SEL, 0); + } + return TS_STATUS_OK; + +} + int32_t hmcad15xx_set_test_pattern(hmcad15xxADC_t* adc, hmcad15xxTestMode_t mode, uint16_t testData1, uint16_t testData2) { if(!adc) diff --git a/src/hmcad15xx.h b/src/hmcad15xx.h index 8b05147..65428a9 100644 --- a/src/hmcad15xx.h +++ b/src/hmcad15xx.h @@ -53,6 +53,7 @@ extern "C" { #define HMCAD15_NUM_ADC (4) #define HMCAD15_NUM_CHANNELS (4) +#define HMCAD15_NUM_BRANCHES (8) #define HMCAD15_FULL_SCALE_SET(x) (((((x+2) - HMCAD15_FULL_SCALE_MIN) * 0x3F) / \ (HMCAD15_FULL_SCALE_MAX - HMCAD15_FULL_SCALE_MIN)) & 0x3F) @@ -78,6 +79,9 @@ extern "C" { #define HMCAD15_SEL_CH_3(x) (((x) & 0x0F) << 1) #define HMCAD15_SEL_CH_4(x) (((x) & 0x0F) << 9) +#define HMCAD15_CGAIN_MODE_X (0x1 << 0) +#define HMCAD15_FGAIN_EN (0x1 << 1) + #define HMCAD15_CGAIN_Q1(x) ((x) & 0xF) #define HMCAD15_CGAIN_Q2(x) (((x) & 0xF) << 4) #define HMCAD15_CGAIN_Q3(x) (((x) & 0xF) << 8) @@ -195,7 +199,6 @@ typedef struct hmcad15xxChCfg_s uint8_t active; uint8_t input; uint8_t coarse; - uint8_t fine; uint8_t invert; } hmcad15xxChCfg_t; @@ -207,6 +210,7 @@ typedef struct hmcad15xxADC_s hmcad15xxBtcFmt_t format; hmcad15xxDataWidth_t width; int32_t fullScale_x10; + uint8_t fineCal[HMCAD15_NUM_BRANCHES]; uint8_t clockDiv; uint8_t lvdsPhase; uint8_t drive; @@ -259,6 +263,16 @@ int32_t hmcad15xx_set_channel_config(hmcad15xxADC_t* adc); */ int32_t hmcad15xx_full_scale_adjust(hmcad15xxADC_t* adc, int8_t adjustment); +/** + * @brief Update settings for the branch fine gain control + * + * @param adc Pointer to an ADC instance + * @param enable Enable or Disable the fine gain adjustments + * @return int32_t TS_STATUS_OK if successful, TS_INVALID_PARAM if the gain settings + * could not be applied. + */ +int32_t hmcad15xx_fine_gain_set(hmcad15xxADC_t* adc, bool enable); + /** * @brief Put the HMCAD15xx into a test mode that sends test data out the LVDS * interface diff --git a/src/thunderscope.c b/src/thunderscope.c index 23a41d5..8a9f0e5 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -226,7 +226,7 @@ int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolut return TS_STATUS_ERROR; } -int32_t thunderscopeCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal) +int32_t thunderscopeChanCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal) { ts_inst_t* pInst = (ts_inst_t*)ts; @@ -238,7 +238,7 @@ int32_t thunderscopeCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCal return TS_STATUS_ERROR; } -int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal) +int32_t thunderscopeChanCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal) { ts_inst_t* pInst = (ts_inst_t*)ts; @@ -250,6 +250,30 @@ int32_t thunderscopeCalibrationGet(tsHandle_t ts, uint32_t channel, tsChannelCal return TS_STATUS_ERROR; } +int32_t thunderscopeAdcCalibrationSet(tsHandle_t ts, tsAdcCalibration_t *cal) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + return ts_channel_adc_calibration_set(pInst->pChannel, cal); + } + + return TS_STATUS_ERROR; +} + +int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t *cal) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + return ts_channel_adc_calibration_get(pInst->pChannel, cal); + } + + return TS_STATUS_ERROR; +} + int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl) { ts_inst_t* pInst = (ts_inst_t*)ts; diff --git a/src/ts_channel.c b/src/ts_channel.c index 848e0e5..b49e839 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -266,7 +266,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) for(uint32_t chanIdx = 0; chanIdx < TS_NUM_CHANNELS; chanIdx++) { pChan->chan[chanIdx].channelNo = chanIdx; - ts_adc_set_gain(&pChan->adc, chanIdx, TS_ADC_CH_COARSE_GAIN_DEFAULT, TS_ADC_CH_FINE_GAIN_DEFAULT); + ts_adc_set_gain(&pChan->adc, chanIdx, TS_ADC_CH_COARSE_GAIN_DEFAULT); retVal = ts_adc_set_channel_conf(&pChan->adc, chanIdx, g_channelConf[chanIdx].adc_input, g_channelConf[chanIdx].adc_invert); if(retVal != TS_STATUS_OK) @@ -660,6 +660,36 @@ int32_t ts_channel_calibration_get(tsChannelHdl_t tsChannels, uint32_t chanIdx, return TS_STATUS_OK; } +int32_t ts_channel_adc_calibration_set(tsChannelHdl_t tsChannels, tsAdcCalibration_t* cal) +{ + ts_channel_t* ts = (ts_channel_t*)tsChannels; + if(tsChannels == NULL || cal == NULL) + { + LOG_ERROR("Invalid handle"); + return TS_STATUS_ERROR; + } + + LOG_DEBUG("Received Calibration for ADC"); + LOG_DEBUG("\tFine 2-1: %02X %02X", cal->branchFineGain[1], cal->branchFineGain[0]); + LOG_DEBUG("\tFine 4-3: %02X %02X", cal->branchFineGain[3], cal->branchFineGain[2]); + LOG_DEBUG("\tFine 6-5: %02X %02X", cal->branchFineGain[5], cal->branchFineGain[4]); + LOG_DEBUG("\tFine 8-7: %02X %02X", cal->branchFineGain[7], cal->branchFineGain[6]); + + return ts_adc_cal_set(&ts->adc, cal); +} + +int32_t ts_channel_adc_calibration_get(tsChannelHdl_t tsChannels, tsAdcCalibration_t* cal) +{ + ts_channel_t* ts = (ts_channel_t*)tsChannels; + if(tsChannels == NULL || cal == NULL) + { + LOG_ERROR("Invalid handle"); + return TS_STATUS_ERROR; + } + + return ts_adc_cal_get(&ts->adc, cal); +} + int32_t ts_channel_calibration_manual(tsChannelHdl_t tsChannels, uint32_t chanIdx, tsChannelCtrl_t ctrl) { int32_t retVal = TS_STATUS_OK; diff --git a/src/ts_channel.h b/src/ts_channel.h index f0a6bc8..d479f0f 100644 --- a/src/ts_channel.h +++ b/src/ts_channel.h @@ -106,6 +106,24 @@ int32_t ts_channel_calibration_set(tsChannelHdl_t tsChannels, uint32_t chanIdx, */ int32_t ts_channel_calibration_get(tsChannelHdl_t tsChannels, uint32_t chanIdx, tsChannelCalibration_t* cal); +/** + * @brief Set the calibration parameters for the adc + * + * @param tsChannels Thunderscope Channel handle + * @param cal Pointer to the calibration structure to apply + * @return int32_t TS_STATUS_OK on success, else TS_STATUS_ERROR + */ +int32_t ts_channel_adc_calibration_set(tsChannelHdl_t tsChannels, tsAdcCalibration_t* cal); + +/** + * @brief Get the calibration parameters for the adc + * + * @param tsChannels Thunderscope Channel handle + * @param cal Pointer to the calibration structure + * @return int32_t TS_STATUS_OK on success, else TS_STATUS_ERROR + */ + int32_t ts_channel_adc_calibration_get(tsChannelHdl_t tsChannels, tsAdcCalibration_t* cal); + /** * @brief Manually set the AFE controls for a channel * From ab48da3982e23903c939025dda82979543a83acd Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 29 May 2025 07:18:29 -0400 Subject: [PATCH 20/83] Initial cal test app --- example/CMakeLists.txt | 9 + example/thunderscope_cal.cpp | 742 +++++++++++++++++++++++++++++++++++ 2 files changed, 751 insertions(+) create mode 100644 example/thunderscope_cal.cpp diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index da522c6..0bafb80 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -17,3 +17,12 @@ target_link_libraries(thunderscope_fw tslitex_static) if(WIN32) target_link_libraries(thunderscope_fw setupapi) endif() + +# Thunderscope Cal CLI +add_executable(thunderscope_cal thunderscope_cal.cpp) + +target_link_libraries(thunderscope_cal tslitex_static) + +if(WIN32) + target_link_libraries(thunderscope_cal setupapi) +endif() diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp new file mode 100644 index 0000000..bcb1d55 --- /dev/null +++ b/example/thunderscope_cal.cpp @@ -0,0 +1,742 @@ +/** + * This file is part of libtslitex. + * + * Copyright (c) 2020-2021 Florent Kermarrec + * Copyright (c) 2022 Franck Jullien + * Copyright (c) 2020 Antmicro + * Copyright (c) 2024 John Simons + * Copyright (c) 2024 Nate Meyer + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#ifdef _WIN32 +#include +#endif + +#define OPTPARSE_IMPLEMENTATION +#include "optparse.h" + +#include "../src/mcp443x.h" +#include "../src/platform.h" + +#include "liblitepcie.h" +#include "thunderscope.h" +#include "ts_calibration.h" + +#ifdef _WIN32 +#define FILE_FLAGS (FILE_ATTRIBUTE_NORMAL) +#else +#define FILE_FLAGS (O_RDWR) +#endif + +#define FLUSH() { char c; do { c = getchar(); } while(c != '\n' && c != 'EOF');} + +/* Variables */ +/*-----------*/ +static tsScopeCalibration_t calibration; + +/* Main */ +/*------*/ +// Functioning returning +// current time +auto now() +{ + return std::chrono::steady_clock::now(); +} + +// Function calculating sleep time +// with 500ms delay +auto awake_time() +{ + using std::chrono::operator"" ms; + return now() + 500ms; +} + +static void cal_get_average_value(tsHandle_t pTs, uint8_t chanBitmap, uint32_t numSamples, int8_t* pAvgResult) +{ + //Capture average + //Setup and Enable Channels + tsChannelParam_t chConfig = {0}; + uint8_t numChan = 0; + uint8_t channel = 0; + while(chanBitmap > 0) + { + if(chanBitmap & 0x1) + { + thunderscopeChannelConfigGet(pTs, channel, &chConfig); + chConfig.active = 1; + thunderscopeChannelConfigSet(pTs, channel, &chConfig); + } + channel++; + chanBitmap >>= 1; + } + if(numChan > 2) + numChan = 4; + + int8_t* sampleBuffer = (int8_t*)calloc(((numSamples*numChan) + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE, DMA_BUFFER_SIZE); + uint64_t sampleLen = 0; + + uint64_t data_sum = 0; + //Start Sample capture + thunderscopeDataEnable(pTs, 1); + + if(sampleBuffer != NULL) + { + //Collect Samples + int32_t readRes = thunderscopeRead(pTs, (uint8_t*)sampleBuffer, numSamples); + if(readRes < 0) + { + printf("ERROR: Sample Get Buffers failed with %" PRIi32, readRes); + } + else if(readRes != numSamples) + { + printf("WARN: Read returned different number of bytes, %" PRIu32 " / %" PRIu32 "\r\n", readRes, numSamples); + } + else + { + sampleLen = readRes; + } + } + + //Stop Samples + thunderscopeDataEnable(pTs, 0); + + if(sampleLen > 0) + { + int64_t avg[TS_NUM_CHANNELS] = {0}; + int64_t idx = 0; + while (idx < sampleLen) + { + avg[0] += sampleBuffer[idx++]; + if(numChan > 1) + { + avg[1] += sampleBuffer[idx++]; + if(numChan > 2) + { + avg[2] += sampleBuffer[idx++]; + avg[3] += sampleBuffer[idx++]; + } + } + } + pAvgResult[0] = (int8_t)(avg[0] / sampleLen); + pAvgResult[1] = (int8_t)(avg[1] / sampleLen); + pAvgResult[2] = (int8_t)(avg[2] / sampleLen); + pAvgResult[3] = (int8_t)(avg[3] / sampleLen); + + } + + //Disable channels + for(uint8_t i=0; i < TS_NUM_CHANNELS; i++) + { + thunderscopeChannelConfigGet(pTs, i, &chConfig); + chConfig.active = 0; + thunderscopeChannelConfigSet(pTs, i, &chConfig); + } + free(sampleBuffer); +} + +static void cal_get_interleaved_average(tsHandle_t pTs, uint32_t numSamples, int8_t branchAvg[8]) +{ + //Capture average + //Setup and Enable Channels + tsChannelParam_t chConfig = {0}; + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + thunderscopeChannelConfigGet(pTs, i, &chConfig); + if(i==0) + { + chConfig.active = 1; + } + else + { + chConfig.active = 0; + } + thunderscopeChannelConfigSet(pTs, i, &chConfig); + } + + + int8_t* sampleBuffer = (int8_t*)calloc(DMA_BUFFER_SIZE, (numSamples + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE); + int64_t sampleLen = 0; + + //Start Sample capture + thunderscopeDataEnable(pTs, 1); + + if(sampleBuffer != NULL) + { + //Collect Samples + int32_t readRes = thunderscopeRead(pTs, (uint8_t*)sampleBuffer, numSamples); + if(readRes < 0) + { + printf("ERROR: Sample Get Buffers failed with %" PRIi32 "\r\n", readRes); + } + else if(readRes != numSamples) + { + printf("WARN: Read returned different number of bytes, %" PRIu32 " / %" PRIu32 "\r\n", readRes, numSamples); + } + else + { + sampleLen = readRes; + } + } + + //Stop Samples + thunderscopeDataEnable(pTs, 0); + + int64_t avg[8] = {0}; + uint64_t idx = 0; + while (idx < sampleLen) + { + avg[0] += sampleBuffer[idx++]; + avg[1] += sampleBuffer[idx++]; + avg[2] += sampleBuffer[idx++]; + avg[3] += sampleBuffer[idx++]; + avg[4] += sampleBuffer[idx++]; + avg[5] += sampleBuffer[idx++]; + avg[6] += sampleBuffer[idx++]; + avg[7] += sampleBuffer[idx++]; + } + printf("Num Samples: %lld\r\n",sampleLen); + for(uint32_t i=0; i < 8; i++) + { + printf("\t Branch %d Sum: %lld\r\n",i, avg[i]); + } + //Branch Order from HMCAD1520 Datasheet, Table 27 + branchAvg[0] = (int8_t)(avg[0] / (sampleLen / 8)); + branchAvg[5] = (int8_t)(avg[1] / (sampleLen / 8)); + branchAvg[1] = (int8_t)(avg[2] / (sampleLen / 8)); + branchAvg[4] = (int8_t)(avg[3] / (sampleLen / 8)); + branchAvg[7] = (int8_t)(avg[4] / (sampleLen / 8)); + branchAvg[2] = (int8_t)(avg[5] / (sampleLen / 8)); + branchAvg[6] = (int8_t)(avg[6] / (sampleLen / 8)); + branchAvg[3] = (int8_t)(avg[7] / (sampleLen / 8)); + + //Disable channels + for(uint8_t i=0; i < TS_NUM_CHANNELS; i++) + { + thunderscopeChannelConfigGet(pTs, i, &chConfig); + chConfig.active = 0; + thunderscopeChannelConfigSet(pTs, i, &chConfig); + } + free(sampleBuffer); +} + +static void cal_step_0(tsHandle_t pTs, uint8_t chanBitmap) +{ + //Set Channels to unity gain, no offset + tsChannelParam_t param = {0}; + param.active = false; + param.volt_scale_uV = 700000; + param.volt_offset_uV = 0; + param.bandwidth = 0; + param.coupling = TS_COUPLE_AC; + param.term = TS_TERM_1M; + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + thunderscopeChannelConfigSet(pTs, i, ¶m); + } + } + return; +} + +static void cal_step_1(uint8_t chanBitmap) +{ + // Measure +VBIAS + printf("Measure the +VBIAS Test Point\r\n"); + + while(1) + { + double vbias = 0; + printf("Enter value of +VBIAS: "); + if((scanf("%lf", &vbias) > 0) && (vbias < 5.0)) + { + printf("Saving value of %lli uV for +VBIAS\r\n", (int64_t)(vbias * 1000000)); + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + calibration.afeCal[i].bias_uV = (int32_t)(vbias * 1000000); + } + } + break; + } + printf("Invalid Value for VBIAS\r\n"); + } + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} + + return; +} + +static void cal_step_2(tsHandle_t pTs, uint8_t chanBitmap) +{ + double vtrim[2][TS_NUM_CHANNELS] = {0}; + tsChannelCtrl_t afe_ctrl = {0}; + afe_ctrl.atten = 0; + // Characterize DPOT + //Set DAC and DPOT + afe_ctrl.dac = 1000; + // afe_ctrl.dac = 1 * 4095 / 5; + afe_ctrl.dpot = MCP4432_MAX; + + printf("Setting V_DAC to 1V and R_TRIM to 50k\r\n"); + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + thunderscopeCalibrationManualCtrl(pTs, i, afe_ctrl); + } + } + + // Measure VTRIM + printf("Measure the VTRIM Test Point(s)\r\n"); + + //User input of value and tracking to build calibration + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + while(1) + { + double measure = 0; + printf("Enter value of Channel %d VTRIM: ", i); + if((scanf("%lf", &measure) > 0) && (measure < 5.0)) + { + printf("Storing value of %.03lf V for VTRIM\r\n", measure); + vtrim[0][i] = measure; + break; + } + printf("Invalid Value for VTRIM\r\n"); + } + } + } + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} + + afe_ctrl.dpot = MCP4432_MAX/2; + + printf("Setting V_DAC to 1V and R_TRIM to 25k\r\n"); + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + thunderscopeCalibrationManualCtrl(pTs, i, afe_ctrl); + } + } + + // Measure VTRIM + printf("Measure the VTRIM Test Point(s)\r\n"); + + //User input of value and tracking to build calibration + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + while(1) + { + double measure = 0; + printf("Enter value of Channel %d VTRIM: ", i); + if((scanf("%lf", &measure) > 0) && (measure < 5.0)) + { + printf("Storing value of %.03lf V for VTRIM\r\n", measure); + vtrim[1][i] = measure; + break; + } + printf("Invalid Value for VTRIM\r\n"); + } + } + } + + // Calculate DPOT value + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + calibration.afeCal[i].trimRheostat_range = (500.0 * (2 * vtrim[1][i] - vtrim[0][i] - 1.0)) / (vtrim[0][i] - vtrim[1][i]); + printf("Setting Channel %d Trim DPot to a full range of %d Ohms\r\n", i, calibration.afeCal[i].trimRheostat_range); + } + } + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} +} + +static void cal_step_3(tsHandle_t pTs, uint8_t chanBitmap) +{ + // Characterize Input Bias Current + double vtrim[TS_NUM_CHANNELS] = {0}; + tsChannelCtrl_t afe_ctrl = {0}; + afe_ctrl.atten = 0; + + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + //Set DAC and DPOT + printf("Setting channel %d V_DAC to %duV and R_TRIM to %d Ohm\r\n", i, calibration.afeCal[i].bias_uV, calibration.afeCal[i].trimRheostat_range); + afe_ctrl.dac = (uint16_t)(calibration.afeCal[i].bias_uV); + // afe_ctrl.dac = (int32_t)(calibration.afeCal[i].bias_mV * 4095 / 5); + afe_ctrl.dpot = ((((500) * calibration.afeCal[i].trimRheostat_range) / MCP4432_MAX) + MCP4432_RWIPER); + thunderscopeCalibrationManualCtrl(pTs, i, afe_ctrl); + } + } + + // Measure VTRIM + printf("Measure the VTRIM Test Point(s)\r\n"); + + //User input of value and tracking to build calibration + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + while(1) + { + double measure = 0; + printf("Enter value of Channel %d VTRIM: ", i); + if((scanf("%lf", &measure) > 0) && (measure < 5.0)) + { + calibration.afeCal[i].preampInputBias_uA = (int32_t)(((((double)calibration.afeCal[i].bias_uV/1000000) - measure)/250.0) * 1000000.0); + printf("Saving value of %i uA for Channel %d Preamp Input Bias Current\r\n", calibration.afeCal[i].preampInputBias_uA, i); + break; + } + printf("Invalid Value for VTRIM\r\n"); + } + } + } + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} +} + +static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) +{ + // Characterize Preamp Output Offset + // Set V_trim to 2.5V + // Set Low Gain (no attenuator, 1M termination, Low preamp gain) + tsChannelParam_t param = {0}; + param.active = false; + param.volt_scale_uV = 79000; + param.volt_offset_uV = 0; + param.bandwidth = 0; + param.coupling = TS_COUPLE_AC; + param.term = TS_TERM_1M; + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + thunderscopeChannelConfigSet(pTs, i, ¶m); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Measure Average values + int8_t currentVal[TS_NUM_CHANNELS] = {0}; + cal_get_average_value(pTs, chanBitmap, DMA_BUFFER_SIZE*128, currentVal); + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + // Set average for channel as cal.preampLowOffset_mv + calibration.afeCal[i].preampLowOffset_uV = (int32_t)((((double)currentVal[i]) - 128.0)/700000.0); + printf("Saving value of %i uV for Channel %d Preamp Low-gain Offset\r\n", calibration.afeCal[i].preampLowOffset_uV, i); + } + } + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} + + // Set High Gain (no attenuator, 1M termination, High preamp gain) + param.active = false; + param.volt_scale_uV = 8000; + param.volt_offset_uV = 0; + param.bandwidth = 0; + param.coupling = TS_COUPLE_AC; + param.term = TS_TERM_1M; + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + thunderscopeChannelConfigSet(pTs, i, ¶m); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Measure Average values + cal_get_average_value(pTs, chanBitmap, DMA_BUFFER_SIZE*128, currentVal); + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((chanBitmap >> i) & 0x1) + { + // Set average for channel as cal.preampLowOffset_mv + calibration.afeCal[i].preampHighOffset_uV = (int32_t)((((double)currentVal[i]) - 128.0)/700000.0); + printf("Saving value of %i uV for Channel %d Preamp High-gain Offset\r\n", calibration.afeCal[i].preampHighOffset_uV, i); + } + } + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} +} + +static void cal_fine_gain(tsHandle_t pTs) +{ + // Characterize ADC Branch Offsets + // Set V_trim to 2.5V + // Set Low Gain (no attenuator, 1M termination, Low preamp gain) + // Set 20MHz bandwidth filter + tsChannelParam_t param = {0}; + param.active = false; + param.volt_scale_uV = 8000; + param.volt_offset_uV = 0; + param.bandwidth = 20; + param.coupling = TS_COUPLE_AC; + param.term = TS_TERM_1M; + + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if(i == 0) + { + thunderscopeChannelConfigSet(pTs, i, ¶m); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Measure Average values + int8_t branchVals[8] = {0}; + cal_get_interleaved_average(pTs, DMA_BUFFER_SIZE*8, branchVals); + + int32_t median_sample; + int8_t max_sample = branchVals[0]; + int8_t min_sample = branchVals[0]; + printf("Sampled Branch Averages:\r\n"); + for(uint32_t i=0; i < 8; i++) + { + printf("\t Branch %d: %3d\r\n", i, branchVals[i]); + if(branchVals[i] > max_sample) + { + max_sample = branchVals[i]; + } + if(branchVals[i] < min_sample) + { + min_sample = branchVals[i]; + } + } + + // Find median branch val + + + //Scale branches to match + + + //Print Calibration + + + //Verify Calibration + cal_get_interleaved_average(pTs, DMA_BUFFER_SIZE*8, branchVals); + printf("Calibrated Branch Averages:\r\n"); + for(uint32_t i=0; i < 8; i++) + { + printf("\t Branch %d: %3d\r\n", i, branchVals[i]); + } + + + FLUSH(); + printf("\r\n"); + while(getchar() != '\n') {;;} +} + +#define TS_CAL_STEPS 4 +static void do_calibration(file_t fd, uint32_t idx, uint8_t channelBitmap, uint32_t stepNo) +{ + tsHandle_t tsHdl = thunderscopeOpen(idx, false); + + //Load Starting Config or set Default + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((channelBitmap >> i) & 0x1) + { + calibration.afeCal[i].buffer_uV = TS_VBUFFER_NOMINAL_UV; + calibration.afeCal[i].bias_uV = TS_VBIAS_NOMINAL_UV; + calibration.afeCal[i].attenuatorGain1M_mdB = TS_ATTENUATION_1M_GAIN_mdB; + calibration.afeCal[i].attenuatorGain50_mdB = TS_TERMINATION_50OHM_GAIN_mdB; + calibration.afeCal[i].bufferGain_mdB = TS_BUFFER_GAIN_NOMINAL_mdB; + calibration.afeCal[i].trimRheostat_range = MCP4432_503_FULL_SCALE_OHM; + calibration.afeCal[i].preampLowGainError_mdB = 0; + calibration.afeCal[i].preampHighGainError_mdB = 0; + calibration.afeCal[i].preampOutputGainError_mdB = 0; + calibration.afeCal[i].preampLowOffset_uV = 0; + calibration.afeCal[i].preampHighOffset_uV = 0; + calibration.afeCal[i].preampInputBias_uA = TS_PREAMP_INPUT_BIAS_CURRENT_uA; + + thunderscopeChanCalibrationSet(tsHdl, i, &calibration.afeCal[i]); + } + } + + + + //Intentially fall through each case to do all calibration steps + switch(stepNo) + { + default: + case 0: + cal_step_0(tsHdl, channelBitmap); + case 1: + cal_step_1(channelBitmap); + case 2: + cal_step_2(tsHdl, channelBitmap); + case 3: + cal_step_3(tsHdl, channelBitmap); + case 4: + cal_step_4(tsHdl, channelBitmap); + case 5: + cal_fine_gain(tsHdl); + } + + //Apply Final Calibration + for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) + { + if((channelBitmap >> i) & 0x1) + { + thunderscopeChanCalibrationSet(tsHdl, i, &calibration.afeCal[i]); + } + } + + thunderscopeClose(tsHdl); +} + +static void print_help(void) +{ + printf("TS Calibration Util Usage:\r\n"); + printf("\t -d Device Index\r\n"); + printf("\t -c Channel bitmap\r\n"); + printf("\t -s Skip to step #\r\n"); +} + +/* Main */ +/*------*/ + +int main(int argc, char** argv) +{ + const char* cmd = argv[0]; + unsigned char fpga_identifier[256]; + char devicePath[TS_IDENT_STR_LEN]; + + + file_t fd; + uint32_t idx = 0; + int i; + uint8_t channelBitmap = 0x01; + uint32_t stepNo = 0; + + struct optparse_long argList[] = { + {"dev", 'd', OPTPARSE_REQUIRED}, + {"chan", 'c', OPTPARSE_REQUIRED}, + {"step", 's', OPTPARSE_REQUIRED}, + {0} + }; + + auto argCount = 1; + char *arg = argv[argCount]; + int option; + struct optparse options; + + (void)argc; + optparse_init(&options, argv); + while ((option = optparse_long(&options, argList, NULL)) != -1) + { + switch (option) { + case 'c': + channelBitmap = strtol(options.optarg, NULL, 0); + argCount+=2; + break; + case 'd': + idx = strtol(options.optarg, NULL, 0); + argCount+=2; + break; + case 's': + stepNo = strtol(options.optarg, NULL, 0); + argCount+=2; + break; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + print_help(); + exit(EXIT_SUCCESS); + } + } + arg = argv[argCount]; + + snprintf(devicePath, TS_IDENT_STR_LEN, LITEPCIE_CTRL_NAME(%d), idx); + printf("Opening Device %s\n", devicePath); + fd = litepcie_open((const char*)devicePath, FILE_FLAGS); + if(fd == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Could not init driver\n"); + exit(1); + } + + + printf("\x1b[1m[> FPGA/SoC Information:\x1b[0m\n"); + printf("------------------------\n"); + + for (i = 0; i < 256; i++) + { + fpga_identifier[i] = litepcie_readl(fd, CSR_IDENTIFIER_MEM_BASE + 4 * i); + } + printf("FPGA Identifier: %s.\n", fpga_identifier); + +#ifdef CSR_DNA_BASE + printf("FPGA DNA: 0x%08x%08x\n", + litepcie_readl(fd, CSR_DNA_ID_ADDR + 4 * 0), + litepcie_readl(fd, CSR_DNA_ID_ADDR + 4 * 1)); +#endif +#ifdef CSR_XADC_BASE + printf("FPGA Temperature: %0.1f �C\n", + (double)litepcie_readl(fd, CSR_XADC_TEMPERATURE_ADDR) * 503.975 / 4096 - 273.15); + printf("FPGA VCC-INT: %0.2f V\n", + (double)litepcie_readl(fd, CSR_XADC_VCCINT_ADDR) / 4096 * 3); + printf("FPGA VCC-AUX: %0.2f V\n", + (double)litepcie_readl(fd, CSR_XADC_VCCAUX_ADDR) / 4096 * 3); + printf("FPGA VCC-BRAM: %0.2f V\n", + (double)litepcie_readl(fd, CSR_XADC_VCCBRAM_ADDR) / 4096 * 3); +#endif + + // Walk the user through calibration steps + do_calibration(fd, idx, channelBitmap, stepNo); + + /* Close LitePCIe device. */ + litepcie_close(fd); + + return 0; +} \ No newline at end of file From 3ed1a3eb36c86ca73e75b1a3a12a5532ac088309 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 30 May 2025 00:38:39 -0400 Subject: [PATCH 21/83] Add branch fine gain calibration calculation --- example/thunderscope_cal.cpp | 105 +++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp index bcb1d55..dd28a05 100644 --- a/example/thunderscope_cal.cpp +++ b/example/thunderscope_cal.cpp @@ -151,7 +151,7 @@ static void cal_get_average_value(tsHandle_t pTs, uint8_t chanBitmap, uint32_t n free(sampleBuffer); } -static void cal_get_interleaved_average(tsHandle_t pTs, uint32_t numSamples, int8_t branchAvg[8]) +static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int64_t branchSum[8]) { //Capture average //Setup and Enable Channels @@ -198,33 +198,20 @@ static void cal_get_interleaved_average(tsHandle_t pTs, uint32_t numSamples, int //Stop Samples thunderscopeDataEnable(pTs, 0); - int64_t avg[8] = {0}; uint64_t idx = 0; while (idx < sampleLen) { - avg[0] += sampleBuffer[idx++]; - avg[1] += sampleBuffer[idx++]; - avg[2] += sampleBuffer[idx++]; - avg[3] += sampleBuffer[idx++]; - avg[4] += sampleBuffer[idx++]; - avg[5] += sampleBuffer[idx++]; - avg[6] += sampleBuffer[idx++]; - avg[7] += sampleBuffer[idx++]; + //Branch Order from HMCAD1520 Datasheet, Table 27 + branchSum[0] += sampleBuffer[idx++]; + branchSum[5] += sampleBuffer[idx++]; + branchSum[1] += sampleBuffer[idx++]; + branchSum[4] += sampleBuffer[idx++]; + branchSum[7] += sampleBuffer[idx++]; + branchSum[2] += sampleBuffer[idx++]; + branchSum[6] += sampleBuffer[idx++]; + branchSum[3] += sampleBuffer[idx++]; } printf("Num Samples: %lld\r\n",sampleLen); - for(uint32_t i=0; i < 8; i++) - { - printf("\t Branch %d Sum: %lld\r\n",i, avg[i]); - } - //Branch Order from HMCAD1520 Datasheet, Table 27 - branchAvg[0] = (int8_t)(avg[0] / (sampleLen / 8)); - branchAvg[5] = (int8_t)(avg[1] / (sampleLen / 8)); - branchAvg[1] = (int8_t)(avg[2] / (sampleLen / 8)); - branchAvg[4] = (int8_t)(avg[3] / (sampleLen / 8)); - branchAvg[7] = (int8_t)(avg[4] / (sampleLen / 8)); - branchAvg[2] = (int8_t)(avg[5] / (sampleLen / 8)); - branchAvg[6] = (int8_t)(avg[6] / (sampleLen / 8)); - branchAvg[3] = (int8_t)(avg[7] / (sampleLen / 8)); //Disable channels for(uint8_t i=0; i < TS_NUM_CHANNELS; i++) @@ -512,6 +499,9 @@ static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) static void cal_fine_gain(tsHandle_t pTs) { + tsAdcCalibration_t cal = {0}; + thunderscopeAdcCalibrationGet(pTs, &cal); + // Characterize ADC Branch Offsets // Set V_trim to 2.5V // Set Low Gain (no attenuator, 1M termination, Low preamp gain) @@ -535,16 +525,16 @@ static void cal_fine_gain(tsHandle_t pTs) std::this_thread::sleep_for(std::chrono::seconds(1)); // Measure Average values - int8_t branchVals[8] = {0}; - cal_get_interleaved_average(pTs, DMA_BUFFER_SIZE*8, branchVals); + int64_t branchVals[8] = {0}; + cal_get_interleaved_samples(pTs, DMA_BUFFER_SIZE*1024, branchVals); - int32_t median_sample; - int8_t max_sample = branchVals[0]; - int8_t min_sample = branchVals[0]; + int64_t median_sample; + int64_t max_sample = branchVals[0]; + int64_t min_sample = branchVals[0]; printf("Sampled Branch Averages:\r\n"); for(uint32_t i=0; i < 8; i++) { - printf("\t Branch %d: %3d\r\n", i, branchVals[i]); + printf("\t Branch %d: %lld\r\n", i+1, branchVals[i]); if(branchVals[i] > max_sample) { max_sample = branchVals[i]; @@ -556,29 +546,70 @@ static void cal_fine_gain(tsHandle_t pTs) } // Find median branch val - + median_sample = (max_sample + min_sample) / 2; + printf("Branch median value: %lld\r\n", median_sample); //Scale branches to match - - + for(uint32_t i=0; i < 8; i++) + { + uint8_t config; + double branchScale = (double)median_sample/(double)branchVals[i]; + printf("Branch %d differs from the median by a factor of %.8f\r\n", i+1, branchScale); + if(branchScale > 1) + { + branchScale = (branchScale - 1) * 8192; + // Round and limit to 63 + config = branchScale > 63 ? 63 : (uint8_t)(branchScale + 0.5); + } + else + { + branchScale = (1 - branchScale) * 8192; + // Round and limit to 63 + config = branchScale > 63 ? 63 : (uint8_t)(branchScale + 0.5); + // Invert + config = ~config; + } + cal.branchFineGain[i] = config & 0x7F; + } + //Print Calibration - + for(int i=0; i<8; i++) + { + printf("Branch %d Fine Gain Cal: %02X\r\n", i, cal.branchFineGain[i]); + } //Verify Calibration - cal_get_interleaved_average(pTs, DMA_BUFFER_SIZE*8, branchVals); + thunderscopeAdcCalibrationSet(pTs, &cal); + cal_get_interleaved_samples(pTs, DMA_BUFFER_SIZE*1024, branchVals); printf("Calibrated Branch Averages:\r\n"); + max_sample = branchVals[0]; + min_sample = branchVals[0]; + for(uint32_t i=0; i < 8; i++) + { + printf("\t Branch %d: %lld\r\n", i+1, branchVals[i]); + if(branchVals[i] > max_sample) + { + max_sample = branchVals[i]; + } + if(branchVals[i] < min_sample) + { + min_sample = branchVals[i]; + } + } + + median_sample = (max_sample + min_sample) / 2; for(uint32_t i=0; i < 8; i++) { - printf("\t Branch %d: %3d\r\n", i, branchVals[i]); + uint8_t config; + double branchScale = (double)branchVals[i] / median_sample; + printf("Calibrated Branch %d differs from the median by a factor of %.8f\r\n", i, branchScale); } - FLUSH(); printf("\r\n"); while(getchar() != '\n') {;;} } -#define TS_CAL_STEPS 4 static void do_calibration(file_t fd, uint32_t idx, uint8_t channelBitmap, uint32_t stepNo) { tsHandle_t tsHdl = thunderscopeOpen(idx, false); From eecd143c07c9e69092b11862d13e09733f0e7713 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Mon, 2 Jun 2025 22:47:20 -0400 Subject: [PATCH 22/83] Fix build errors on linux --- example/thunderscope_cal.cpp | 1 + src/platform.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp index dd28a05..1a96d83 100644 --- a/example/thunderscope_cal.cpp +++ b/example/thunderscope_cal.cpp @@ -34,6 +34,7 @@ #include "../src/mcp443x.h" #include "../src/platform.h" +#include "../src/util.h" #include "liblitepcie.h" #include "thunderscope.h" diff --git a/src/platform.h b/src/platform.h index fbb1c31..1c6ab81 100644 --- a/src/platform.h +++ b/src/platform.h @@ -186,7 +186,7 @@ extern const flash_layout_t ts_64Mb_layout; #define TS_FLASH_64M_MFG (0xC2) //Macronix #define TS_FLASH_64M_ID (0x2537) //64Mb SPI Flash -inline bool isBetaDevice(file_t ts) { return (0 == (litepcie_readl(ts, CSR_DEV_STATUS_HW_ID_ADDR) & (1 << CSR_DEV_STATUS_HW_ID_HW_VALID_OFFSET)));} +static inline bool isBetaDevice(file_t ts) { return (0 == (litepcie_readl(ts, CSR_DEV_STATUS_HW_ID_ADDR) & (1 << CSR_DEV_STATUS_HW_ID_HW_VALID_OFFSET)));} #ifdef __cplusplus } From 9072b991c91d96ed7c6a27eaafe730114c946dab Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 3 Jun 2025 20:25:44 -0400 Subject: [PATCH 23/83] Fix malloc error and cleanup cal functions --- example/thunderscope_cal.cpp | 62 ++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp index 1a96d83..4fb16ea 100644 --- a/example/thunderscope_cal.cpp +++ b/example/thunderscope_cal.cpp @@ -73,6 +73,7 @@ static void cal_get_average_value(tsHandle_t pTs, uint8_t chanBitmap, uint32_t n { //Capture average //Setup and Enable Channels + uint8_t* sampleBuffer = NULL; tsChannelParam_t chConfig = {0}; uint8_t numChan = 0; uint8_t channel = 0; @@ -86,11 +87,12 @@ static void cal_get_average_value(tsHandle_t pTs, uint8_t chanBitmap, uint32_t n } channel++; chanBitmap >>= 1; + numChan++; } if(numChan > 2) numChan = 4; - int8_t* sampleBuffer = (int8_t*)calloc(((numSamples*numChan) + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE, DMA_BUFFER_SIZE); + sampleBuffer = (uint8_t*)calloc(((numSamples*numChan) + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE, DMA_BUFFER_SIZE); uint64_t sampleLen = 0; uint64_t data_sum = 0; @@ -100,10 +102,10 @@ static void cal_get_average_value(tsHandle_t pTs, uint8_t chanBitmap, uint32_t n if(sampleBuffer != NULL) { //Collect Samples - int32_t readRes = thunderscopeRead(pTs, (uint8_t*)sampleBuffer, numSamples); + int32_t readRes = thunderscopeRead(pTs, sampleBuffer, numSamples); if(readRes < 0) { - printf("ERROR: Sample Get Buffers failed with %" PRIi32, readRes); + printf("ERROR: Sample Get Buffers failed with %" PRIi32"\r\n", readRes); } else if(readRes != numSamples) { @@ -156,6 +158,7 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int { //Capture average //Setup and Enable Channels + uint8_t* sampleBuffer = NULL; tsChannelParam_t chConfig = {0}; for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) { @@ -172,7 +175,7 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int } - int8_t* sampleBuffer = (int8_t*)calloc(DMA_BUFFER_SIZE, (numSamples + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE); + sampleBuffer = (uint8_t*)calloc(DMA_BUFFER_SIZE, (numSamples + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE); int64_t sampleLen = 0; //Start Sample capture @@ -181,7 +184,7 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int if(sampleBuffer != NULL) { //Collect Samples - int32_t readRes = thunderscopeRead(pTs, (uint8_t*)sampleBuffer, numSamples); + int32_t readRes = thunderscopeRead(pTs, sampleBuffer, numSamples); if(readRes < 0) { printf("ERROR: Sample Get Buffers failed with %" PRIi32 "\r\n", readRes); @@ -203,14 +206,14 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int while (idx < sampleLen) { //Branch Order from HMCAD1520 Datasheet, Table 27 - branchSum[0] += sampleBuffer[idx++]; - branchSum[5] += sampleBuffer[idx++]; - branchSum[1] += sampleBuffer[idx++]; - branchSum[4] += sampleBuffer[idx++]; - branchSum[7] += sampleBuffer[idx++]; - branchSum[2] += sampleBuffer[idx++]; - branchSum[6] += sampleBuffer[idx++]; - branchSum[3] += sampleBuffer[idx++]; + branchSum[0] += sampleBuffer[idx++]; //D1A + branchSum[5] += sampleBuffer[idx++]; //D1B + branchSum[1] += sampleBuffer[idx++]; //D2A + branchSum[4] += sampleBuffer[idx++]; //D2B + branchSum[7] += sampleBuffer[idx++]; //D3A + branchSum[2] += sampleBuffer[idx++]; //D3B + branchSum[6] += sampleBuffer[idx++]; //D4A + branchSum[3] += sampleBuffer[idx++]; //D4B } printf("Num Samples: %lld\r\n",sampleLen); @@ -268,8 +271,8 @@ static void cal_step_1(uint8_t chanBitmap) } printf("Invalid Value for VBIAS\r\n"); } - FLUSH(); + printf("\r\n"); while(getchar() != '\n') {;;} @@ -317,10 +320,10 @@ static void cal_step_2(tsHandle_t pTs, uint8_t chanBitmap) } printf("Invalid Value for VTRIM\r\n"); } + FLUSH(); } } - FLUSH(); printf("\r\n"); while(getchar() != '\n') {;;} @@ -356,6 +359,7 @@ static void cal_step_2(tsHandle_t pTs, uint8_t chanBitmap) } printf("Invalid Value for VTRIM\r\n"); } + FLUSH(); } } @@ -369,7 +373,6 @@ static void cal_step_2(tsHandle_t pTs, uint8_t chanBitmap) } } - FLUSH(); printf("\r\n"); while(getchar() != '\n') {;;} } @@ -415,10 +418,9 @@ static void cal_step_3(tsHandle_t pTs, uint8_t chanBitmap) } printf("Invalid Value for VTRIM\r\n"); } + FLUSH(); } } - - FLUSH(); printf("\r\n"); while(getchar() != '\n') {;;} } @@ -459,7 +461,6 @@ static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) } } - FLUSH(); printf("\r\n"); while(getchar() != '\n') {;;} @@ -492,8 +493,7 @@ static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) printf("Saving value of %i uV for Channel %d Preamp High-gain Offset\r\n", calibration.afeCal[i].preampHighOffset_uV, i); } } - - FLUSH(); + printf("\r\n"); while(getchar() != '\n') {;;} } @@ -554,6 +554,16 @@ static void cal_fine_gain(tsHandle_t pTs) for(uint32_t i=0; i < 8; i++) { uint8_t config; + /** + * branch * cal_scale = median + * ... cal_scale = median / branch + * we want to find the abs delta from 1, our resolution is 2^-13 + * cal_scale = 1 + x/8192 + * ... x = (cal_scale - 1) * 8192 + * for scale < 1, value is negative + * ... -x = -(cal_scale - 1) * 8192 + * ... x = (1 - cal_scale) * 8192 + */ double branchScale = (double)median_sample/(double)branchVals[i]; printf("Branch %d differs from the median by a factor of %.8f\r\n", i+1, branchScale); if(branchScale > 1) @@ -606,12 +616,11 @@ static void cal_fine_gain(tsHandle_t pTs) printf("Calibrated Branch %d differs from the median by a factor of %.8f\r\n", i, branchScale); } - FLUSH(); printf("\r\n"); while(getchar() != '\n') {;;} } -static void do_calibration(file_t fd, uint32_t idx, uint8_t channelBitmap, uint32_t stepNo) +static void do_calibration(uint32_t idx, uint8_t channelBitmap, uint32_t stepNo) { tsHandle_t tsHdl = thunderscopeOpen(idx, false); @@ -763,12 +772,11 @@ int main(int argc, char** argv) printf("FPGA VCC-BRAM: %0.2f V\n", (double)litepcie_readl(fd, CSR_XADC_VCCBRAM_ADDR) / 4096 * 3); #endif - - // Walk the user through calibration steps - do_calibration(fd, idx, channelBitmap, stepNo); - /* Close LitePCIe device. */ litepcie_close(fd); + // Walk the user through calibration steps + do_calibration(idx, channelBitmap, stepNo); + return 0; } \ No newline at end of file From 03f6522776e4f7e2fc46579fdad93e88ecf8f9bc Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 6 Jun 2025 21:47:10 -0400 Subject: [PATCH 24/83] Fix preamp gain manual control. Set jitter ctrl for performance in HMCAD. --- example/thunderscope_cal.cpp | 25 +------------------------ src/afe.c | 11 +++++++++++ src/hmcad15xx.c | 3 +++ src/platform.h | 12 ++++++++++++ src/ts_channel.c | 2 +- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp index 4fb16ea..e4fdf6a 100644 --- a/example/thunderscope_cal.cpp +++ b/example/thunderscope_cal.cpp @@ -159,28 +159,13 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int //Capture average //Setup and Enable Channels uint8_t* sampleBuffer = NULL; - tsChannelParam_t chConfig = {0}; - for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) - { - thunderscopeChannelConfigGet(pTs, i, &chConfig); - if(i==0) - { - chConfig.active = 1; - } - else - { - chConfig.active = 0; - } - thunderscopeChannelConfigSet(pTs, i, &chConfig); - } - sampleBuffer = (uint8_t*)calloc(DMA_BUFFER_SIZE, (numSamples + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE); int64_t sampleLen = 0; //Start Sample capture thunderscopeDataEnable(pTs, 1); - + if(sampleBuffer != NULL) { //Collect Samples @@ -215,15 +200,7 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int branchSum[6] += sampleBuffer[idx++]; //D4A branchSum[3] += sampleBuffer[idx++]; //D4B } - printf("Num Samples: %lld\r\n",sampleLen); - //Disable channels - for(uint8_t i=0; i < TS_NUM_CHANNELS; i++) - { - thunderscopeChannelConfigGet(pTs, i, &chConfig); - chConfig.active = 0; - thunderscopeChannelConfigSet(pTs, i, &chConfig); - } free(sampleBuffer); } diff --git a/src/afe.c b/src/afe.c index 0d13544..d850f58 100644 --- a/src/afe.c +++ b/src/afe.c @@ -74,6 +74,17 @@ int32_t ts_afe_init(ts_afe_t* afe, uint8_t channel, spi_dev_t afe_amp, i2c_t tri afe->cal.preampLowOffset_uV = 0; afe->cal.preampHighOffset_uV = 0; afe->cal.preampInputBias_uA = TS_PREAMP_INPUT_BIAS_CURRENT_uA; + afe->cal.preampAttenuatorGain_mdB[0] = TS_AFE_PREAMP_ATTEN_0_mdB; + afe->cal.preampAttenuatorGain_mdB[1] = TS_AFE_PREAMP_ATTEN_1_mdB; + afe->cal.preampAttenuatorGain_mdB[2] = TS_AFE_PREAMP_ATTEN_2_mdB; + afe->cal.preampAttenuatorGain_mdB[3] = TS_AFE_PREAMP_ATTEN_3_mdB; + afe->cal.preampAttenuatorGain_mdB[4] = TS_AFE_PREAMP_ATTEN_4_mdB; + afe->cal.preampAttenuatorGain_mdB[5] = TS_AFE_PREAMP_ATTEN_5_mdB; + afe->cal.preampAttenuatorGain_mdB[6] = TS_AFE_PREAMP_ATTEN_6_mdB; + afe->cal.preampAttenuatorGain_mdB[7] = TS_AFE_PREAMP_ATTEN_7_mdB; + afe->cal.preampAttenuatorGain_mdB[8] = TS_AFE_PREAMP_ATTEN_8_mdB; + afe->cal.preampAttenuatorGain_mdB[9] = TS_AFE_PREAMP_ATTEN_9_mdB; + afe->cal.preampAttenuatorGain_mdB[10] = TS_AFE_PREAMP_ATTEN_10_mdB; Mcp4728ChannelConfig_t trimConf = {0}; trimConf.vref = MCP4728_VREF_VDD; diff --git a/src/hmcad15xx.c b/src/hmcad15xx.c index 37fb009..eaec545 100644 --- a/src/hmcad15xx.c +++ b/src/hmcad15xx.c @@ -57,6 +57,9 @@ int32_t hmcad15xx_init(hmcad15xxADC_t* adc, spi_dev_t dev) //Gain dB mode hmcad15xxRegWrite(adc, HMCAD15_REG_GAIN_SEL, 0); + //Set Jitter Ctrl + hmcad15xxRegWrite(adc, HMCAD15_REG_CLK_JITTER, 0xFF); + //Channel Conf hmcad15xxApplySampleMode(adc); hmcad15xxApplyChannelMap(adc); diff --git a/src/platform.h b/src/platform.h index 1c6ab81..3e2201d 100644 --- a/src/platform.h +++ b/src/platform.h @@ -40,6 +40,18 @@ extern "C" { #define TS_BIAS_RESISTOR_NOMINAL (500) #define TS_PREAMP_INPUT_BIAS_CURRENT_uA (40) +#define TS_AFE_PREAMP_ATTEN_0_mdB (0) +#define TS_AFE_PREAMP_ATTEN_1_mdB (-2000) +#define TS_AFE_PREAMP_ATTEN_2_mdB (-4000) +#define TS_AFE_PREAMP_ATTEN_3_mdB (-6000) +#define TS_AFE_PREAMP_ATTEN_4_mdB (-8000) +#define TS_AFE_PREAMP_ATTEN_5_mdB (-10000) +#define TS_AFE_PREAMP_ATTEN_6_mdB (-12000) +#define TS_AFE_PREAMP_ATTEN_7_mdB (-14000) +#define TS_AFE_PREAMP_ATTEN_8_mdB (-16000) +#define TS_AFE_PREAMP_ATTEN_9_mdB (-18000) +#define TS_AFE_PREAMP_ATTEN_10_mdB (-20000) + #define TS_SPI_BUS_BASE_ADDR (CSR_SPIBUS_SPI0_CONTROL_ADDR) #define TS_SPI_BUS_BETA_CS_NUM (5) #define TS_SPI_BUS_DEV_CS_NUM (4) diff --git a/src/ts_channel.c b/src/ts_channel.c index b49e839..7ee6638 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -745,7 +745,7 @@ int32_t ts_channel_calibration_manual(tsChannelHdl_t tsChannels, uint32_t chanId lmh6518Config_t preamp = LMH6518_CONFIG_INIT; preamp.atten = ctrl.pga_atten; preamp.filter = ctrl.pga_bw; - preamp.preamp = ctrl.pga_high_gain = 0 ? PREAMP_LG : PREAMP_HG; + preamp.preamp = ctrl.pga_high_gain == 0 ? PREAMP_LG : PREAMP_HG; preamp.pm = PM_AUX_HIZ; retVal = lmh6518_apply_config(ts->chan[chanIdx].afe.amp, preamp); From 621dd313e6665ec60b0ce76be2538656257e8685 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 7 Jun 2025 09:29:21 -0400 Subject: [PATCH 25/83] Adjust fine gain process and add std dev printout. --- example/thunderscope_cal.cpp | 95 +++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp index e4fdf6a..bec54e2 100644 --- a/example/thunderscope_cal.cpp +++ b/example/thunderscope_cal.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -158,9 +159,9 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int { //Capture average //Setup and Enable Channels - uint8_t* sampleBuffer = NULL; + int8_t* sampleBuffer = NULL; - sampleBuffer = (uint8_t*)calloc(DMA_BUFFER_SIZE, (numSamples + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE); + sampleBuffer = (int8_t*)calloc(DMA_BUFFER_SIZE, (numSamples + DMA_BUFFER_SIZE-1)/DMA_BUFFER_SIZE); int64_t sampleLen = 0; //Start Sample capture @@ -169,7 +170,7 @@ static void cal_get_interleaved_samples(tsHandle_t pTs, uint32_t numSamples, int if(sampleBuffer != NULL) { //Collect Samples - int32_t readRes = thunderscopeRead(pTs, sampleBuffer, numSamples); + int32_t readRes = thunderscopeRead(pTs, (uint8_t*)sampleBuffer, numSamples); if(readRes < 0) { printf("ERROR: Sample Get Buffers failed with %" PRIi32 "\r\n", readRes); @@ -432,7 +433,7 @@ static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) { if((chanBitmap >> i) & 0x1) { - // Set average for channel as cal.preampLowOffset_mv + // Set average for channel as cal.preampLowOffset_uv calibration.afeCal[i].preampLowOffset_uV = (int32_t)((((double)currentVal[i]) - 128.0)/700000.0); printf("Saving value of %i uV for Channel %d Preamp Low-gain Offset\r\n", calibration.afeCal[i].preampLowOffset_uV, i); } @@ -465,7 +466,7 @@ static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) { if((chanBitmap >> i) & 0x1) { - // Set average for channel as cal.preampLowOffset_mv + // Set average for channel as cal.preampLowOffset_uv calibration.afeCal[i].preampHighOffset_uV = (int32_t)((((double)currentVal[i]) - 128.0)/700000.0); printf("Saving value of %i uV for Channel %d Preamp High-gain Offset\r\n", calibration.afeCal[i].preampHighOffset_uV, i); } @@ -477,39 +478,42 @@ static void cal_step_4(tsHandle_t pTs, uint8_t chanBitmap) static void cal_fine_gain(tsHandle_t pTs) { + //Zero Fine branch cal tsAdcCalibration_t cal = {0}; thunderscopeAdcCalibrationGet(pTs, &cal); - + memset(cal.branchFineGain, 0, sizeof(cal.branchFineGain)); + thunderscopeAdcCalibrationSet(pTs, &cal); + // Characterize ADC Branch Offsets - // Set V_trim to 2.5V - // Set Low Gain (no attenuator, 1M termination, Low preamp gain) + // Set Offset to 0.4V + // Set Volt scale to 1.0V + // Set AC coupled, 50Ohm // Set 20MHz bandwidth filter - tsChannelParam_t param = {0}; - param.active = false; - param.volt_scale_uV = 8000; - param.volt_offset_uV = 0; - param.bandwidth = 20; - param.coupling = TS_COUPLE_AC; - param.term = TS_TERM_1M; - - for(uint32_t i=0; i < TS_NUM_CHANNELS; i++) - { - if(i == 0) - { - thunderscopeChannelConfigSet(pTs, i, ¶m); - } - } + tsChannelParam_t chan; + chan.active = 1; + chan.bandwidth = 20; + chan.coupling = TS_COUPLE_AC; + chan.term = TS_TERM_50; + chan.volt_offset_uV = -400000; + chan.volt_scale_uV = 1000000; + thunderscopeChannelConfigSet(pTs, 0, &chan); std::this_thread::sleep_for(std::chrono::seconds(1)); + printf("Collecting ADC Samples....\r\n"); + // Measure Average values int64_t branchVals[8] = {0}; - cal_get_interleaved_samples(pTs, DMA_BUFFER_SIZE*1024, branchVals); + + //Accumulate many times + cal_get_interleaved_samples(pTs, DMA_BUFFER_SIZE*2000, branchVals); int64_t median_sample; + int64_t sum = 0; + double mean = 0, std_dev = 0; int64_t max_sample = branchVals[0]; int64_t min_sample = branchVals[0]; - printf("Sampled Branch Averages:\r\n"); + printf("Sampled Branch Sum:\r\n"); for(uint32_t i=0; i < 8; i++) { printf("\t Branch %d: %lld\r\n", i+1, branchVals[i]); @@ -521,11 +525,19 @@ static void cal_fine_gain(tsHandle_t pTs) { min_sample = branchVals[i]; } + sum += branchVals[i]; } // Find median branch val median_sample = (max_sample + min_sample) / 2; printf("Branch median value: %lld\r\n", median_sample); + mean = sum / 8.0; + for(uint32_t i = 0; i < 8; i++) + { + std_dev += pow((double)branchVals[i] - mean, 2); + } + std_dev = sqrt(std_dev/8.0); + printf("Uncalibrated STD_Dev: %0.12f\r\n", std_dev); //Scale branches to match for(uint32_t i=0; i < 8; i++) @@ -542,16 +554,16 @@ static void cal_fine_gain(tsHandle_t pTs) * ... x = (1 - cal_scale) * 8192 */ double branchScale = (double)median_sample/(double)branchVals[i]; - printf("Branch %d differs from the median by a factor of %.8f\r\n", i+1, branchScale); + printf("Branch %d differs from the median by a factor of %.12f\r\n", i+1, branchScale); if(branchScale > 1) { - branchScale = (branchScale - 1) * 8192; + branchScale = (branchScale - 1) * 8192.0; // Round and limit to 63 config = branchScale > 63 ? 63 : (uint8_t)(branchScale + 0.5); } else { - branchScale = (1 - branchScale) * 8192; + branchScale = (1 - branchScale) * 8192.0; // Round and limit to 63 config = branchScale > 63 ? 63 : (uint8_t)(branchScale + 0.5); // Invert @@ -563,15 +575,21 @@ static void cal_fine_gain(tsHandle_t pTs) //Print Calibration for(int i=0; i<8; i++) { - printf("Branch %d Fine Gain Cal: %02X\r\n", i, cal.branchFineGain[i]); + printf("Branch %d Fine Gain Cal: %02X\r\n", i+1, cal.branchFineGain[i]); } //Verify Calibration - thunderscopeAdcCalibrationSet(pTs, &cal); - cal_get_interleaved_samples(pTs, DMA_BUFFER_SIZE*1024, branchVals); - printf("Calibrated Branch Averages:\r\n"); + memset(branchVals, 0, sizeof(branchVals)); + // thunderscopeAdcCalibrationSet(pTs, &cal); + cal_get_interleaved_samples(pTs, DMA_BUFFER_SIZE*2000, branchVals); + chan.active = 0; + thunderscopeChannelConfigSet(pTs, 0, &chan); + + printf("Calibrated Branch Sum:\r\n"); max_sample = branchVals[0]; min_sample = branchVals[0]; + sum = 0; + std_dev = 0; for(uint32_t i=0; i < 8; i++) { printf("\t Branch %d: %lld\r\n", i+1, branchVals[i]); @@ -583,14 +601,25 @@ static void cal_fine_gain(tsHandle_t pTs) { min_sample = branchVals[i]; } + sum += branchVals[i]; } + // Find median branch val median_sample = (max_sample + min_sample) / 2; + printf("Branch median value: %lld\r\n", median_sample); + mean = sum / 8.0; + for(uint32_t i = 0; i < 8; i++) + { + std_dev += pow((double)branchVals[i] - mean, 2); + } + std_dev = sqrt(std_dev/8.0); + printf("Calibrated STD_Dev: %0.12f\r\n", std_dev); + for(uint32_t i=0; i < 8; i++) { uint8_t config; double branchScale = (double)branchVals[i] / median_sample; - printf("Calibrated Branch %d differs from the median by a factor of %.8f\r\n", i, branchScale); + printf("Calibrated Branch %d differs from the median by a factor of %.12f\r\n", i+1, branchScale); } printf("\r\n"); From 2e95820180963e86413eb1a592c3bed3261236cf Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 8 Jun 2025 17:39:04 -0400 Subject: [PATCH 26/83] Remove REQUIRED on python package --- bindings/python/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index c8b9a34..0525660 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.20) find_package( Python COMPONENTS Interpreter Development.Module - REQUIRED ) add_custom_command( From 0f123e65cd95082b5ec54ce229251e255c7a6683 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 22 Jun 2025 13:44:47 -0400 Subject: [PATCH 27/83] Default to polling mode for now --- src/samples.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/samples.c b/src/samples.c index 88e1d25..18ff1fc 100644 --- a/src/samples.c +++ b/src/samples.c @@ -136,7 +136,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ #elif defined(__APPLE__) while(retVal < (int32_t)bufferLen) { - #if 0 //POLL + #if 1 //POLL litepcie_dma_writer(&inst->dma, inst->active, &inst->dma_buffer_count, &inst->driver_buffer_count, From dd37aba3ea4608ef0179105bd84c859cd343feea Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 28 Jun 2025 20:26:32 -0400 Subject: [PATCH 28/83] Add initial CI/CD script (#23) --- .github/workflows/build_cmake.yml | 88 +++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/build_cmake.yml diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml new file mode 100644 index 0000000..4365a68 --- /dev/null +++ b/.github/workflows/build_cmake.yml @@ -0,0 +1,88 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: Build with CMake + +on: + push: + branches: [ "main", "dev*" ] + pull_request: + branches: [ "main", "dev*" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following configurations: + # 1. + # 2. + # 3. + # 4. + ## + matrix: + os: [ubuntu-latest, windows-latest] + # os: [ubuntu-latest, windows-latest, macos-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + # - os: macos-latest + # c_compiler: clang + # cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + # - os: macos-latest + # c_compiler: cl + # - os: macos-latest + # c_compiler: gcc + + steps: + - uses: actions/checkout@v4 + + - name: Set Vars + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + echo "artifact-name=libtslitex-${{ matrix.os }}-${{ matrix.c_compiler }}-artifacts" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --clean-first --config ${{ matrix.build_type }} + + # - name: Test + # working-directory: ${{ steps.strings.outputs.build-output-dir }} + # # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # run: ctest --build-config ${{ matrix.build_type }} + + - uses: actions/upload-artifact@v4 + with: + name: ${{ steps.strings.outputs.artifact-name }} + path: ${{ steps.strings.outputs.build-output-dir }}/artifacts From 150f4f78f572d8478ffa8902e91d007b661543dc Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 1 Jul 2025 01:03:42 -0400 Subject: [PATCH 29/83] Add targets and actions to build python wheels --- .../{build_cmake.yml => build-cmake.yml} | 0 .github/workflows/build-python-bindings.yml | 68 +++++++++++++++++++ .gitignore | 2 +- CMakeLists.txt | 1 - bindings/python/CMakeLists.txt | 29 +++++++- bindings/python/pyproject.toml.in | 2 +- requirements.txt | 3 +- 7 files changed, 100 insertions(+), 5 deletions(-) rename .github/workflows/{build_cmake.yml => build-cmake.yml} (100%) create mode 100644 .github/workflows/build-python-bindings.yml diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build-cmake.yml similarity index 100% rename from .github/workflows/build_cmake.yml rename to .github/workflows/build-cmake.yml diff --git a/.github/workflows/build-python-bindings.yml b/.github/workflows/build-python-bindings.yml new file mode 100644 index 0000000..aaffef1 --- /dev/null +++ b/.github/workflows/build-python-bindings.yml @@ -0,0 +1,68 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: Build Python Bindings with CMake + +on: + push: + branches: [ "main", "dev*" ] + pull_request: + branches: [ "main", "dev*" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following configurations: + # 1. + # 2. + # 3. + # 4. + ## + matrix: + os: [ubuntu-latest, windows-latest] + # os: [ubuntu-latest, windows-latest, macos-latest] + build_type: [Release] # Note, debug builds are not generated as they require the debug version of Python to be installed. + platform: [auto] + arch: [auto64] + + + steps: + - uses: actions/checkout@v4 + + - name: Set Vars + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build/" >> "$GITHUB_OUTPUT" + echo "build-wheels-dir=${{ github.workspace }}/build/bindings/python/wheelhouse/" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DPY_PLATFORM=${{ matrix.platform }} + -DPY_ARCH=${{ matrix.arch }} + -S ${{ github.workspace }} + + - name: Build Library + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --clean-first --config ${{ matrix.build_type }} -t tslitex_static + + - name: Build Python Wheels + # Generate Cython code. + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --target PyWheels + + + # - name: Test + # working-directory: ${{ steps.strings.outputs.build-output-dir }} + # # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # run: ctest --build-config ${{ matrix.build_type }} + + - uses: actions/upload-artifact@v4 + with: + path: ${{ steps.strings.outputs.build-wheels-dir }}/*.whl diff --git a/.gitignore b/.gitignore index 0151929..a811f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -build* +build/* artifacts/* .vscode .venv \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d6b5a8..b98b2fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,5 +119,4 @@ add_subdirectory(example) # BINDINGS #### add_subdirectory(bindings/python) -add_dependencies(PyBindings tslitex_static litepcie) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 0525660..2c84b5c 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -12,6 +12,20 @@ add_custom_command( DEPENDS tslitex.pyx tslitex.pxd VERBATIM ) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h + COMMAND ${CMAKE_COMMAND} -E copy_directory + "$" + "${CMAKE_CURRENT_BINARY_DIR}/lib" + DEPENDS tslitex_static +) + +add_custom_target(pydeps + DEPENDS tslitex/tslitex.c + ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h + tslitex_static +) set(PY_BIND_LIBS tslitex_static litepcie) if(WIN32) @@ -26,6 +40,19 @@ add_custom_target(PyBindings COMMAND Python::Interpreter -m pipx run build --wheel . WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Building python whl" - DEPENDS tslitex/tslitex.c + DEPENDS pydeps tslitex_static ) +if (NOT DEFINED PY_PLATFORM) + set(PY_PLATFORM "auto") +endif() +if (NOT DEFINED PY_ARCH) + set(PY_ARCH "auto64") +endif() + +add_custom_target(PyWheels + COMMAND Python::Interpreter -m cibuildwheel --platform ${PY_PLATFORM} --archs ${PY_ARCH} --config-file pyproject.toml + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMENT "Building all Python wheels" + DEPENDS pydeps tslitex_static + ) diff --git a/bindings/python/pyproject.toml.in b/bindings/python/pyproject.toml.in index 48f854f..2c0c7f4 100644 --- a/bindings/python/pyproject.toml.in +++ b/bindings/python/pyproject.toml.in @@ -14,5 +14,5 @@ dependencies = ["numpy"] [tool.setuptools] packages = ["tslitex"] ext-modules = [ - { name = "tslitex.tslitex", sources = ["tslitex/tslitex.c"], libraries = ["@PY_EXT_LIBS@"], library-dirs = ["@CMAKE_ARCHIVE_OUTPUT_DIRECTORY@"], include-dirs = ["@CMAKE_LIBRARY_OUTPUT_DIRECTORY@/include"]} + { name = "tslitex.tslitex", sources = ["tslitex/tslitex.c"], libraries = ["@PY_EXT_LIBS@"], library-dirs = ["lib"], include-dirs = ["lib/include"]} ] diff --git a/requirements.txt b/requirements.txt index cf5a04b..7c1b358 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ cython -pipx \ No newline at end of file +pipx +cibuildwheel \ No newline at end of file From 4524b1a9565ce2b00668882e63a838cb384f0913 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 1 Jul 2025 01:13:14 -0400 Subject: [PATCH 30/83] Fix setup of python env in ci/cd --- .github/workflows/build-python-bindings.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/build-python-bindings.yml b/.github/workflows/build-python-bindings.yml index aaffef1..89ace22 100644 --- a/.github/workflows/build-python-bindings.yml +++ b/.github/workflows/build-python-bindings.yml @@ -33,6 +33,13 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Setup Python Environment + uses: actions/setup-python@v5 + with: + python-version: '3.13' + cache: 'pip' + - run: pip install -r ${{ github.workspace }}/requirements.txt + - name: Set Vars id: strings shell: bash From 220fbd3a65291ec858f3f29d000bcf6d86108fad Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 1 Jul 2025 12:47:41 -0400 Subject: [PATCH 31/83] Build release config of wheels --- .github/workflows/build-python-bindings.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-python-bindings.yml b/.github/workflows/build-python-bindings.yml index 89ace22..61adf00 100644 --- a/.github/workflows/build-python-bindings.yml +++ b/.github/workflows/build-python-bindings.yml @@ -60,15 +60,8 @@ jobs: run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --clean-first --config ${{ matrix.build_type }} -t tslitex_static - name: Build Python Wheels - # Generate Cython code. - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --target PyWheels - - - # - name: Test - # working-directory: ${{ steps.strings.outputs.build-output-dir }} - # # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - # run: ctest --build-config ${{ matrix.build_type }} + # Build Wheel from Cython code. + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} -t PyWheels - uses: actions/upload-artifact@v4 with: From 298901d297da3e6b1946d68fd9677dc1327ad2b6 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 1 Jul 2025 14:02:15 -0400 Subject: [PATCH 32/83] Give wheel artifacts unique name --- .github/workflows/build-python-bindings.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-python-bindings.yml b/.github/workflows/build-python-bindings.yml index 61adf00..a6fb14b 100644 --- a/.github/workflows/build-python-bindings.yml +++ b/.github/workflows/build-python-bindings.yml @@ -65,4 +65,5 @@ jobs: - uses: actions/upload-artifact@v4 with: + name: wheel-${{ matrix.os }}-${{ matrix.arch }} path: ${{ steps.strings.outputs.build-wheels-dir }}/*.whl From 92f794ee2e3695dd106ab478d447d26e78808d18 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 3 Jul 2025 00:39:13 -0400 Subject: [PATCH 33/83] Add versioning scheme to library --- CMakeLists.txt | 9 ++- bindings/python/CMakeLists.txt | 5 ++ bindings/python/pyproject.toml.in | 4 +- version.cmake | 97 +++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 version.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b98b2fd..1f05427 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.22) project(libtslitex) +# Set Version +include(version.cmake) + enable_language(C) set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD_REQUIRED ON) # error if compiler doesn't support c17 @@ -83,7 +86,7 @@ add_library(tslitex SHARED ${TS_SOURCES} ${TS_LIB_HEADERS} ) - +set_target_properties(tslitex PROPERTIES VERSION ${${PROJECT_NAME}_VERSION}) set_target_properties(tslitex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> @@ -99,6 +102,7 @@ add_library(tslitex_static STATIC ${TS_SOURCES} ${TS_LIB_HEADERS} ) +set_target_properties(tslitex_static PROPERTIES VERSION ${${PROJECT_NAME}_VERSION}) set_target_properties(tslitex_static PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> @@ -113,6 +117,9 @@ target_include_directories(tslitex_static PUBLIC include) file(COPY ${TS_LIB_HEADERS} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/include) +#### +# EXAMPLE APPLICATIONS +#### add_subdirectory(example) #### diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 2c84b5c..9373841 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -33,6 +33,11 @@ if(WIN32) endif() list(JOIN PY_BIND_LIBS "\", \"" PY_EXT_LIBS) +set(PY_WHL_VERSION_STRING "${${PROJECT_NAME}_VERSION}") +if (NOT ${${PROJECT_NAME}_VERSION_AHEAD} EQUAL "0") + set(PY_WHL_VERSION_STRING "${PY_WHL_VERSION_STRING}.dev${${PROJECT_NAME}_VERSION_AHEAD}") +endif() + configure_file(pyproject.toml.in pyproject.toml @ONLY) configure_file(__init__.py.in tslitex/__init__.py COPYONLY) diff --git a/bindings/python/pyproject.toml.in b/bindings/python/pyproject.toml.in index 2c0c7f4..ab6a846 100644 --- a/bindings/python/pyproject.toml.in +++ b/bindings/python/pyproject.toml.in @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta" [project] name = "tslitex" -version = "0.0.1" -license = {text = "BSD-2-Clause"} +version = "@PY_WHL_VERSION_STRING@" +license = "BSD-2-Clause" authors = [ {name = "Nate Meyer", email = "nate.devel@gmail.com"}, ] diff --git a/version.cmake b/version.cmake new file mode 100644 index 0000000..79a3b5d --- /dev/null +++ b/version.cmake @@ -0,0 +1,97 @@ +# +# This cmake module sets the project version and partial version +# variables by analysing the git tag and commit history. It expects git +# tags defined with semantic versioning 2.0.0 (http://semver.org/). +# +# The module expects the PROJECT_NAME variable to be set, and recognizes +# the GIT_FOUND, GIT_EXECUTABLE and VERSION_UPDATE_FROM_GIT variables. +# If Git is found and VERSION_UPDATE_FROM_GIT is set to boolean TRUE, +# the project version will be updated using information fetched from the +# most recent git tag and commit. Otherwise, the module will try to read +# a VERSION file containing the full and partial versions. The module +# will update this file each time the project version is updated. +# +# Once done, this module will define the following variables: +# +# ${PROJECT_NAME}_VERSION_STRING - Version string without metadata +# such as "v2.0.0" or "v1.2.41-beta.1". This should correspond to the +# most recent git tag. +# ${PROJECT_NAME}_VERSION_STRING_FULL - Version string with metadata +# such as "v2.0.0+3.a23fbc" or "v1.3.1-alpha.2+4.9c4fd1" +# ${PROJECT_NAME}_VERSION - Same as ${PROJECT_NAME}_VERSION_STRING, +# without the preceding 'v', e.g. "2.0.0" or "1.2.41-beta.1" +# ${PROJECT_NAME}_VERSION_MAJOR - Major version integer (e.g. 2 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_MINOR - Minor version integer (e.g. 3 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_PATCH - Patch version integer (e.g. 1 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_TWEAK - Tweak version string (e.g. "RC.2" in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_AHEAD - How many commits ahead of last tag (e.g. 21 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_GIT_SHA - The git sha1 of the most recent commit (e.g. the "ef12c8" in v2.3.1-RC.2+21.ef12c8) +# +# This module is public domain, use it as it fits you best. +# +# Author: Nuno Fachada + +find_package(Git) + +# Check if git is found... +if (GIT_FOUND) + + # Get last tag from git + execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE) + + #How many commits since last tag + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list main ${${PROJECT_NAME}_VERSION_STRING}^..HEAD --count + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_AHEAD + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get current commit SHA from git + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_GIT_SHA + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get partial versions into a list + string(REGEX MATCHALL "-.*$|[0-9]+" ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${${PROJECT_NAME}_VERSION_STRING}) + + # Set the version numbers + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 0 ${PROJECT_NAME}_VERSION_MAJOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 1 ${PROJECT_NAME}_VERSION_MINOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 2 ${PROJECT_NAME}_VERSION_PATCH) + + # The tweak part is optional, so check if the list contains it + list(LENGTH ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN) + if (${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN GREATER 3) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST 3 ${PROJECT_NAME}_VERSION_TWEAK) + string(SUBSTRING ${${PROJECT_NAME}_VERSION_TWEAK} 1 -1 ${PROJECT_NAME}_VERSION_TWEAK) + endif() + + # Unset the list + unset(${PROJECT_NAME}_PARTIAL_VERSION_LIST) + + # Set full project version string + set(${PROJECT_NAME}_VERSION_STRING_FULL + ${${PROJECT_NAME}_VERSION_STRING}+${${PROJECT_NAME}_VERSION_AHEAD}.${${PROJECT_NAME}_VERSION_GIT_SHA}) + +else() + + # Git not available, get version from file + set(${PROJECT_NAME}_VERSION_STRING_FULL "unknown-version") + +endif() + + +# Set project version (without the preceding 'v') +message(STATUS "Setting ${PROJECT_NAME} version to: ${${PROJECT_NAME}_VERSION_STRING_FULL}") +set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}) +if (${PROJECT_NAME}_VERSION_TWEAK) + set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION}-${${PROJECT_NAME}_VERSION_TWEAK}) +endif() \ No newline at end of file From 96c617c3c8759a155cef3a32770c5771c5a03d07 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 20 Jul 2025 01:03:35 -0400 Subject: [PATCH 34/83] Some cleanup of macos sample reads --- litepcie/src/litepcie_dma.c | 4 +--- litepcie/src/litepcie_helpers.c | 3 +-- src/samples.c | 15 ++++++++------- src/thunderscope.c | 2 ++ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index b9c3bf5..508f48a 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -83,9 +83,7 @@ uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint size_t outlen = sizeof(m); #endif checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_LOCK, m)); - //TODO - // return m.dma_reader_status; - return 1; + return m.dma_reader_status; } void litepcie_release_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer) { diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 2fa7ba1..94d82f2 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -184,8 +184,7 @@ file_t litepcie_open(const char* name, int32_t flags) fd = CreateFile(devName, (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, flags, NULL); #elif defined(__APPLE__) -static const char* dextIdentifier = "litepcie"; - + static const char* dextIdentifier = "litepcie"; kern_return_t ret = kIOReturnSuccess; io_iterator_t iterator = IO_OBJECT_NULL; io_service_t service = IO_OBJECT_NULL; diff --git a/src/samples.c b/src/samples.c index 18ff1fc..6595be3 100644 --- a/src/samples.c +++ b/src/samples.c @@ -41,6 +41,7 @@ #define TS_DMA_NAME_LEN (16) #define TS_DMA_NAME_ARGS(chan, dev) "litepcie" #define TS_DMA_OS_FLAGS (O_CLOEXEC) +#define APPLE_MMAP_DMA #elif defined(__LINUX__) #define TS_DMA_NAME "/dev/thunderscope%u" #define TS_DMA_NAME_LEN (24) @@ -70,18 +71,18 @@ int32_t samples_init(sampleStream_t* inst, uint8_t devIdx, uint8_t channel) { litepcie_dma_set_loopback(&inst->dma, 0); retVal = TS_STATUS_OK; -#if defined(__APPLE__) +#if defined(APPLE_MMAP_DMA) mach_vm_address_t writerAddress = 0; mach_vm_size_t writerSize = 0; IOConnectMapMemory64(inst->dma.fds.fd, LITEPCIE_DMA_WRITER | 0, mach_task_self(), &writerAddress, &writerSize, kIOMapAnywhere); - if (writerAddress != NULL) { + if (writerAddress != 0 && writerSize > 0) { inst->dma.buf_rd = (uint8_t*)writerAddress; } else { printf("failed to acquire writer mapped buffer"); } -#endif +#endif //APPLE_MMAP_DMA } //else, DMA Unavailable } @@ -136,7 +137,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ #elif defined(__APPLE__) while(retVal < (int32_t)bufferLen) { - #if 1 //POLL + #if defined (APPLE_MMAP_DMA) //POLLING litepcie_dma_writer(&inst->dma, inst->active, &inst->dma_buffer_count, &inst->driver_buffer_count, @@ -155,7 +156,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ } else { - NS_DELAY(250); + NS_DELAY(10000); //10us } #else size_t readLen = bufferLen - retVal; @@ -170,7 +171,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ } //Actual Read Length gets returned in the transfer struct retVal += sampleTransfer.length; - #endif + #endif //APPLE_MMAP_DMA } #else while (retVal < bufferLen) @@ -221,7 +222,7 @@ int32_t samples_teardown(sampleStream_t* inst) &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); -#if defined(__APPLE__) +#if defined(APPLE_MMAP_DMA) IOConnectUnmapMemory(inst->dma.fds.fd, LITEPCIE_DMA_WRITER | 0, mach_task_self(), (mach_vm_address_t)inst->dma.buf_rd); diff --git a/src/thunderscope.c b/src/thunderscope.c index 8a9f0e5..cdca231 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -169,6 +169,8 @@ int32_t thunderscopeClose(tsHandle_t ts) { samples_teardown(&pInst->samples); ts_channel_destroy(pInst->pChannel); + gpio_group_set(pInst->status_leds, 0); + } litepcie_close(pInst->ctrl); free(pInst); From d37b2fc2d1af229ce4720c3335233beacdeb2e7b Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 20 Jul 2025 01:21:12 -0400 Subject: [PATCH 35/83] Add macos to CI actions. Cleanup misc build warnings. --- .github/workflows/build-cmake.yml | 17 ++++++++--------- litepcie/src/litepcie_dma.c | 4 ++-- src/hmcad15xx.c | 10 ++++++++-- src/spiflash.c | 4 ++-- src/ts_fw_manager.c | 4 ++-- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-cmake.yml b/.github/workflows/build-cmake.yml index 4365a68..bad81da 100644 --- a/.github/workflows/build-cmake.yml +++ b/.github/workflows/build-cmake.yml @@ -23,8 +23,7 @@ jobs: # 4. ## matrix: - os: [ubuntu-latest, windows-latest] - # os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest] build_type: [Release] c_compiler: [gcc, clang, cl] include: @@ -37,9 +36,9 @@ jobs: - os: ubuntu-latest c_compiler: clang cpp_compiler: clang++ - # - os: macos-latest - # c_compiler: clang - # cpp_compiler: clang++ + - os: macos-latest + c_compiler: clang + cpp_compiler: clang++ exclude: - os: windows-latest c_compiler: gcc @@ -47,10 +46,10 @@ jobs: c_compiler: clang - os: ubuntu-latest c_compiler: cl - # - os: macos-latest - # c_compiler: cl - # - os: macos-latest - # c_compiler: gcc + - os: macos-latest + c_compiler: cl + - os: macos-latest + c_compiler: gcc steps: - uses: actions/checkout@v4 diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index 508f48a..c237eec 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -382,7 +382,7 @@ char *litepcie_dma_next_read_buffer(struct litepcie_dma_ctrl *dma) if (!dma->buffers_available_read) return NULL; dma->buffers_available_read--; - char *ret = dma->buf_rd + dma->usr_read_buf_offset * DMA_BUFFER_SIZE; + char *ret = (char*)dma->buf_rd + dma->usr_read_buf_offset * DMA_BUFFER_SIZE; dma->usr_read_buf_offset = (dma->usr_read_buf_offset + 1) % DMA_BUFFER_COUNT; return ret; } @@ -392,7 +392,7 @@ char *litepcie_dma_next_write_buffer(struct litepcie_dma_ctrl *dma) if (!dma->buffers_available_write) return NULL; dma->buffers_available_write--; - char *ret = dma->buf_wr + dma->usr_write_buf_offset * DMA_BUFFER_SIZE; + char *ret = (char*)dma->buf_wr + dma->usr_write_buf_offset * DMA_BUFFER_SIZE; dma->usr_write_buf_offset = (dma->usr_write_buf_offset + 1) % DMA_BUFFER_COUNT; return ret; } diff --git a/src/hmcad15xx.c b/src/hmcad15xx.c index eaec545..42a8fd9 100644 --- a/src/hmcad15xx.c +++ b/src/hmcad15xx.c @@ -365,7 +365,10 @@ static void hmcad15xxApplyChannelMap(hmcad15xxADC_t* adc) HMCAD15_CH_INVERT_Q2(adc->channelCfg[1].invert) | HMCAD15_CH_INVERT_Q3(adc->channelCfg[2].invert) | HMCAD15_CH_INVERT_Q4(adc->channelCfg[3].invert)); - break; + break; + default: + LOG_ERROR("ADC Mode Not Supported: %d", adc->mode); + return; } hmcad15xxRegWrite(adc, HMCAD15_REG_IN_SEL_1_2, in12); @@ -394,6 +397,9 @@ static void hmcad15xxApplyChannelGain(hmcad15xxADC_t* adc) cgain |= HMCAD15_CGAIN_Q3(adc->channelCfg[2].coarse); cgain |= HMCAD15_CGAIN_Q4(adc->channelCfg[3].coarse); hmcad15xxRegWrite(adc, HMCAD15_REG_COARSE_GAIN_1, cgain); - break; + break; + default: + LOG_ERROR("ADC Mode Not Supported: %d", adc->mode); + break; } } \ No newline at end of file diff --git a/src/spiflash.c b/src/spiflash.c index 5f5a87f..b5a4126 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -264,7 +264,7 @@ int32_t spiflash_erase(spiflash_dev_t* dev, uint32_t addr, uint32_t len) uint32_t flash_word; get_flash_data(dev->fd, (addr+i+j), (uint8_t*)&flash_word, sizeof(uint32_t)); if (flash_word != 0xffffffff) { - LOG_ERROR("Error: location 0x%08lx not erased (0x%08x)", addr+i+j, flash_word); + LOG_ERROR("Error: location 0x%08x not erased (0x%08x)", addr+i+j, flash_word); return TS_STATUS_ERROR; } } @@ -300,7 +300,7 @@ int32_t spiflash_write(spiflash_dev_t* dev, uint32_t addr, const uint8_t *pData, get_flash_data(dev->fd, addr+offset, (uint8_t*)r_buf, w_len); for (j = 0; j < w_len; j++) { if (r_buf[j] != pData[offset+j]) { - LOG_ERROR("Error: verify failed at 0x%08lx (0x%02x should be 0x%02x)", (uint32_t)(addr+offset+j), r_buf[j], pData[offset+j]); + LOG_ERROR("Error: verify failed at 0x%08x (0x%02x should be 0x%02x)", (uint32_t)(addr+offset+j), r_buf[j], pData[offset+j]); spiflash_write_disable(dev->fd); return TS_STATUS_ERROR; } diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index 37042e8..fe5dffd 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -56,7 +56,7 @@ static uint32_t ts_fw_get_idcode(file_t fd) static const char* ts_fw_parse_bit_header(const char* header, const char** part, uint32_t* bin_len) { - const uint8_t* position = header; + const char* position = header; uint16_t key_len = 0; //First Field ('0FF0...') @@ -228,7 +228,7 @@ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_str atomic_store(&mngr->fw_progress, bin_length); // Program New Bitstream - if(bin_length != spiflash_write(&mngr->flash_dev, mngr->partition_table->user_bitstream_start, bin_start, bin_length)) + if(bin_length != spiflash_write(&mngr->flash_dev, mngr->partition_table->user_bitstream_start, (const uint8_t*)bin_start, bin_length)) { LOG_ERROR("Failed to write user bitstream partition"); return TS_STATUS_ERROR; From 5830038693d720aab2a917d30f0be15e7f18f179 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 24 Jul 2025 23:24:44 -0400 Subject: [PATCH 36/83] Update actions. Fix open name. --- .github/workflows/build-cmake.yml | 3 +++ .github/workflows/build-python-bindings.yml | 6 ++++-- CMakeLists.txt | 4 ++++ bindings/python/pyproject.toml.in | 1 - litepcie/CMakeLists.txt | 4 ++-- litepcie/include/litepcie_dma.h | 3 ++- litepcie/src/litepcie_helpers.c | 3 +-- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-cmake.yml b/.github/workflows/build-cmake.yml index bad81da..869fbbe 100644 --- a/.github/workflows/build-cmake.yml +++ b/.github/workflows/build-cmake.yml @@ -53,6 +53,9 @@ jobs: steps: - uses: actions/checkout@v4 + with: + fetch-tags: true + fetch-depth: 0 - name: Set Vars id: strings diff --git a/.github/workflows/build-python-bindings.yml b/.github/workflows/build-python-bindings.yml index a6fb14b..b436d53 100644 --- a/.github/workflows/build-python-bindings.yml +++ b/.github/workflows/build-python-bindings.yml @@ -23,8 +23,7 @@ jobs: # 4. ## matrix: - os: [ubuntu-latest, windows-latest] - # os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest] build_type: [Release] # Note, debug builds are not generated as they require the debug version of Python to be installed. platform: [auto] arch: [auto64] @@ -32,6 +31,9 @@ jobs: steps: - uses: actions/checkout@v4 + with: + fetch-tags: true + fetch-depth: 0 - name: Setup Python Environment uses: actions/setup-python@v5 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8859da0..a2feebe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,10 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") add_definitions(-DUNICODE -D_UNICODE) +IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + add_definitions(-D__LINUX__) +ENDIF() + add_definitions(-DEN_LOGGING) add_compile_definitions("$<$:EN_LOGGING_DEBUG>") diff --git a/bindings/python/pyproject.toml.in b/bindings/python/pyproject.toml.in index ab6a846..2274bb8 100644 --- a/bindings/python/pyproject.toml.in +++ b/bindings/python/pyproject.toml.in @@ -5,7 +5,6 @@ build-backend = "setuptools.build_meta" [project] name = "tslitex" version = "@PY_WHL_VERSION_STRING@" -license = "BSD-2-Clause" authors = [ {name = "Nate Meyer", email = "nate.devel@gmail.com"}, ] diff --git a/litepcie/CMakeLists.txt b/litepcie/CMakeLists.txt index 79cdb4e..cab4efc 100644 --- a/litepcie/CMakeLists.txt +++ b/litepcie/CMakeLists.txt @@ -11,11 +11,11 @@ if(WIN32) set(litepcie_PLATFORM_HEADERS public_h/litepcie_win.h ) -elif(LINUX) +elseif(LINUX) set(litepcie_PLATFORM_HEADERS public_h/litepcie_linux.h ) -elif(APPLE) +elseif(APPLE) set(litepcie_PLATFORM_HEADERS public_h/litepcie_mac.h ) diff --git a/litepcie/include/litepcie_dma.h b/litepcie/include/litepcie_dma.h index 57167ca..69f425d 100644 --- a/litepcie/include/litepcie_dma.h +++ b/litepcie/include/litepcie_dma.h @@ -12,6 +12,7 @@ #define LITEPCIE_LIB_DMA_H #include +#include #include "litepcie_helpers.h" #include "litepcie.h" @@ -22,7 +23,7 @@ typedef struct pollfd_s file_t fd; } pollfd_t; #else -#include +#include typedef struct pollfd pollfd_t; #endif diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 94d82f2..b00eb5b 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -184,7 +184,6 @@ file_t litepcie_open(const char* name, int32_t flags) fd = CreateFile(devName, (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, flags, NULL); #elif defined(__APPLE__) - static const char* dextIdentifier = "litepcie"; kern_return_t ret = kIOReturnSuccess; io_iterator_t iterator = IO_OBJECT_NULL; io_service_t service = IO_OBJECT_NULL; @@ -192,7 +191,7 @@ file_t litepcie_open(const char* name, int32_t flags) fd = IO_OBJECT_NULL; /// - Tag: ClientApp_Connect - ret = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching(dextIdentifier), &iterator); + ret = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching(name), &iterator); if (ret != kIOReturnSuccess) { printf("Unable to find service for identifier with error: 0x%08x.\n", ret); _print_kerr_details(ret); From 8b69d5b3a3238af43d8fac67be02e35d36721fcb Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 30 Jul 2025 01:20:59 -0400 Subject: [PATCH 37/83] Fix sample rate not updating on mode changes --- src/ts_channel.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ts_channel.c b/src/ts_channel.c index 7ee6638..d478b14 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -568,10 +568,10 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin actual_rate = rate * 4; } + ts_adc_run(&ts->adc, 0); + if(actual_rate != ts->pll.clkConf.out_clks[TS_PLL_SAMPLE_CLK_IDX].output_freq) { - ts_adc_run(&ts->adc, 0); - // Apply resolution,rate configuration zl3026x_clk_config_t newConf = ts->pll.clkConf; newConf.out_clks[TS_PLL_SAMPLE_CLK_IDX].output_freq = actual_rate; @@ -591,14 +591,14 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin LOG_ERROR("Failed to generate PLL Configuration: %d", clk_len); return clk_len; } + } - ts->status.adc_sample_rate = rate; - ts->status.adc_sample_resolution = resolution; - ts->status.adc_sample_bits = resolution == 256 ? 8 : 16; + ts->status.adc_sample_rate = rate; + ts->status.adc_sample_resolution = resolution; + ts->status.adc_sample_bits = resolution == 256 ? 8 : 16; - ts_adc_set_sample_mode(&ts->adc, rate, resolution); - ts_adc_run(&ts->adc, ts->status.adc_state); - } + ts_adc_set_sample_mode(&ts->adc, rate, resolution); + ts_adc_run(&ts->adc, ts->status.adc_state); return TS_STATUS_OK; } From be6e77f968e7dda21b78d360537074a4a891b699 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 31 Jul 2025 23:09:45 -0400 Subject: [PATCH 38/83] Cleanup linux macro --- CMakeLists.txt | 4 ---- litepcie/include/litepcie_dma.h | 8 ++++---- litepcie/public_h/litepcie.h | 2 +- litepcie/src/litepcie_dma.c | 2 +- src/samples.c | 2 +- src/util.h | 2 +- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a2feebe..8859da0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,10 +24,6 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") add_definitions(-DUNICODE -D_UNICODE) -IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - add_definitions(-D__LINUX__) -ENDIF() - add_definitions(-DEN_LOGGING) add_compile_definitions("$<$:EN_LOGGING_DEBUG>") diff --git a/litepcie/include/litepcie_dma.h b/litepcie/include/litepcie_dma.h index 69f425d..ff66d91 100644 --- a/litepcie/include/litepcie_dma.h +++ b/litepcie/include/litepcie_dma.h @@ -17,14 +17,14 @@ #include "litepcie_helpers.h" #include "litepcie.h" -#if !defined(__LINUX__) +#if defined(__linux__) +#include +typedef struct pollfd pollfd_t; +#else typedef struct pollfd_s { file_t fd; } pollfd_t; -#else -#include -typedef struct pollfd pollfd_t; #endif struct litepcie_dma_ctrl { diff --git a/litepcie/public_h/litepcie.h b/litepcie/public_h/litepcie.h index db90f73..b1e6e44 100644 --- a/litepcie/public_h/litepcie.h +++ b/litepcie/public_h/litepcie.h @@ -13,7 +13,7 @@ extern "C" { #ifdef _WIN32 #include "litepcie_win.h" -#elif __LINUX__ +#elif __linux__ #include "litepcie_linux.h" #elif __APPLE__ #include "litepcie_mac.h" diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index c237eec..1469fd6 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -119,7 +119,7 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED); //Last char is channel ID. Zero remove char before opening file -#elif defined(__LINUX__) +#elif defined(__linux__) if (dma->use_reader) dma->fds.events |= POLLOUT; if (dma->use_writer) diff --git a/src/samples.c b/src/samples.c index 6595be3..0b22cde 100644 --- a/src/samples.c +++ b/src/samples.c @@ -42,7 +42,7 @@ #define TS_DMA_NAME_ARGS(chan, dev) "litepcie" #define TS_DMA_OS_FLAGS (O_CLOEXEC) #define APPLE_MMAP_DMA -#elif defined(__LINUX__) +#elif defined(__linux__) #define TS_DMA_NAME "/dev/thunderscope%u" #define TS_DMA_NAME_LEN (24) #define TS_DMA_NAME_ARGS(chan, dev) (dev) diff --git a/src/util.h b/src/util.h index 4805439..478e00c 100644 --- a/src/util.h +++ b/src/util.h @@ -19,7 +19,7 @@ extern "C" { #if defined(__APPLE__) #define INVALID_HANDLE_VALUE (IO_OBJECT_NULL) -#elif defined(__LINUX__) +#elif defined(__linux__) #define INVALID_HANDLE_VALUE (-1) #endif From a9ce3ffe76b6c4bf715dd342ec1cadc203d94177 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 15 Aug 2025 02:25:00 -0400 Subject: [PATCH 39/83] Fix clearing bits in gpio group set. --- src/gpio.c | 2 +- src/thunderscope.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gpio.c b/src/gpio.c index 1c5cef4..8ab9a03 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -30,6 +30,6 @@ void gpio_clear(gpio_t gpio) void gpio_group_set(gpio_t gpio, uint32_t set) { - uint32_t value = litepcie_readl(gpio.fd, gpio.reg); + uint32_t value = litepcie_readl(gpio.fd, gpio.reg) & ~gpio.bit_mask; litepcie_writel(gpio.fd, gpio.reg, (value | (set & gpio.bit_mask))); } diff --git a/src/thunderscope.c b/src/thunderscope.c index cdca231..56eef0c 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -169,7 +169,7 @@ int32_t thunderscopeClose(tsHandle_t ts) { samples_teardown(&pInst->samples); ts_channel_destroy(pInst->pChannel); - gpio_group_set(pInst->status_leds, 0); + gpio_group_set(pInst->status_leds, pInst->signals->disabled); } litepcie_close(pInst->ctrl); From 8c8c9a124df55ef7563c7a0b7f424bdb954f6d92 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 19 Aug 2025 00:15:45 -0400 Subject: [PATCH 40/83] Update cmake version and python handling --- bindings/python/CMakeLists.txt | 5 +++ version.cmake | 77 +++++++++++++++++++++------------- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 9373841..dd03a08 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -5,6 +5,8 @@ find_package( COMPONENTS Interpreter Development.Module ) +if(Python_FOUND) + add_custom_command( OUTPUT tslitex/tslitex.c COMMAND Python::Interpreter -m cython @@ -61,3 +63,6 @@ add_custom_target(PyWheels COMMENT "Building all Python wheels" DEPENDS pydeps tslitex_static ) +else() +message(STATUS "Python bindings not available: Python not found") +endif() diff --git a/version.cmake b/version.cmake index 79a3b5d..c0eb998 100644 --- a/version.cmake +++ b/version.cmake @@ -40,42 +40,59 @@ if (GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_STRING - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE GIT_DESCRIBE_RESULT) - #How many commits since last tag - execute_process(COMMAND ${GIT_EXECUTABLE} rev-list main ${${PROJECT_NAME}_VERSION_STRING}^..HEAD --count - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_AHEAD - OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT GIT_DESCRIBE_RESULT EQUAL 0) + set(${PROJECT_NAME}_VERSION_STRING "v0.0.0") + set(${PROJECT_NAME}_VERSION_MAJOR 0) + set(${PROJECT_NAME}_VERSION_MINOR 0) + set(${PROJECT_NAME}_VERSION_PATCH 0) + set(${PROJECT_NAME}_VERSION_TWEAK "") + set(${PROJECT_NAME}_VERSION_AHEAD 0) + set(${PROJECT_NAME}_VERSION_GIT_SHA "unknown") + else() + #How many commits since last tag + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list main ${${PROJECT_NAME}_VERSION_STRING}^..HEAD --count + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_AHEAD + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE GIT_REVLIST_RESULT) + + if (NOT GIT_REVLIST_RESULT EQUAL 0) + set(${PROJECT_NAME}_VERSION_AHEAD 0) + endif() - # Get current commit SHA from git - execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_GIT_SHA - OUTPUT_STRIP_TRAILING_WHITESPACE) + # Get current commit SHA from git + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_GIT_SHA + OUTPUT_STRIP_TRAILING_WHITESPACE) - # Get partial versions into a list - string(REGEX MATCHALL "-.*$|[0-9]+" ${PROJECT_NAME}_PARTIAL_VERSION_LIST - ${${PROJECT_NAME}_VERSION_STRING}) + # Get partial versions into a list + string(REGEX MATCHALL "-.*$|[0-9]+" ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${${PROJECT_NAME}_VERSION_STRING}) - # Set the version numbers - list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST - 0 ${PROJECT_NAME}_VERSION_MAJOR) - list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST - 1 ${PROJECT_NAME}_VERSION_MINOR) - list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST - 2 ${PROJECT_NAME}_VERSION_PATCH) + # Set the version numbers + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 0 ${PROJECT_NAME}_VERSION_MAJOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 1 ${PROJECT_NAME}_VERSION_MINOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 2 ${PROJECT_NAME}_VERSION_PATCH) - # The tweak part is optional, so check if the list contains it - list(LENGTH ${PROJECT_NAME}_PARTIAL_VERSION_LIST - ${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN) - if (${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN GREATER 3) - list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST 3 ${PROJECT_NAME}_VERSION_TWEAK) - string(SUBSTRING ${${PROJECT_NAME}_VERSION_TWEAK} 1 -1 ${PROJECT_NAME}_VERSION_TWEAK) - endif() + # The tweak part is optional, so check if the list contains it + list(LENGTH ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN) + if (${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN GREATER 3) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST 3 ${PROJECT_NAME}_VERSION_TWEAK) + string(SUBSTRING ${${PROJECT_NAME}_VERSION_TWEAK} 1 -1 ${PROJECT_NAME}_VERSION_TWEAK) + endif() - # Unset the list - unset(${PROJECT_NAME}_PARTIAL_VERSION_LIST) + # Unset the list + unset(${PROJECT_NAME}_PARTIAL_VERSION_LIST) + endif() # Set full project version string set(${PROJECT_NAME}_VERSION_STRING_FULL From b1539b11a796e844b1bc259e691e06435d55f79b Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 29 Aug 2025 22:58:50 -0400 Subject: [PATCH 41/83] Fix fw update mem leak. Add bitstream update status log --- example/thunderscope_fw.cpp | 2 +- src/thunderscope.c | 21 ++++++++++++++------- src/ts_fw_manager.c | 3 +++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 67da820..1ae779b 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -53,7 +53,7 @@ static void fw_upgrade(tsHandle_t ts, const char* file_path) // Close File file.close(); - + delete bitstream; } else { diff --git a/src/thunderscope.c b/src/thunderscope.c index 56eef0c..c214b6a 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -169,9 +169,9 @@ int32_t thunderscopeClose(tsHandle_t ts) { samples_teardown(&pInst->samples); ts_channel_destroy(pInst->pChannel); - gpio_group_set(pInst->status_leds, pInst->signals->disabled); - } + + gpio_group_set(pInst->status_leds, pInst->signals->disabled); litepcie_close(pInst->ctrl); free(pInst); @@ -346,14 +346,21 @@ int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) { ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t status = TS_STATUS_ERROR; if(pInst) { - return ts_fw_manager_user_fw_update(&pInst->fw, bitstream, len); - } - else - { - return TS_STATUS_ERROR; + status = ts_fw_manager_user_fw_update(&pInst->fw, bitstream, len); + if(status == TS_STATUS_OK) + { + LOG_DEBUG("Bitstream Update Complete"); + } + else + { + LOG_ERROR("Bitstream Update Failed: %d", status); + } } + + return TS_STATUS_ERROR; } diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index fe5dffd..c0e7201 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -228,6 +228,9 @@ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_str atomic_store(&mngr->fw_progress, bin_length); // Program New Bitstream + // TODO: AMD Recommends programming the SYNC word last, so program the bitstream from end to start + // Ref: AMD AR 58090 [https://adaptivesupport.amd.com/s/article/58090] + // Ref: AMD AR 58249 [https://adaptivesupport.amd.com/s/article/58249] if(bin_length != spiflash_write(&mngr->flash_dev, mngr->partition_table->user_bitstream_start, (const uint8_t*)bin_start, bin_length)) { LOG_ERROR("Failed to write user bitstream partition"); From 9adfb639007c64eeb5681b60805c375506589a26 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 29 Aug 2025 23:02:41 -0400 Subject: [PATCH 42/83] Allow attenuator and 50-Ohm termination at the same time for Rev5 units. Fix indexing error in adc channels. --- src/adc.c | 2 +- src/afe.c | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/adc.c b/src/adc.c index 9c7b1a7..5a5523b 100644 --- a/src/adc.c +++ b/src/adc.c @@ -126,9 +126,9 @@ int32_t ts_adc_channel_enable(ts_adc_t* adc, uint8_t channel, uint8_t enable) { //Disable Unused channels in config LOG_DEBUG("Disable CH %d", i); + inactiveCount++; adc->adcDev.channelCfg[TS_NUM_CHANNELS - inactiveCount] = adc->tsChannels[i]; adc->adcDev.channelCfg[TS_NUM_CHANNELS - inactiveCount].active = 0; - inactiveCount++; } } diff --git a/src/afe.c b/src/afe.c index d850f58..89934c1 100644 --- a/src/afe.c +++ b/src/afe.c @@ -120,15 +120,24 @@ int32_t ts_afe_set_gain(ts_afe_t* afe, int32_t gain_mdB) //Remove Preamp Output gain calibration value gain_request -= afe->cal.preampOutputGainError_mdB; - // If 50-Ohm mode in use, limit gain to TBD - if(afe->termination == TS_TERM_50) + if(isBetaDevice(afe->termPin.fd)) { - gain_request -= afe->cal.attenuatorGain50_mdB; + // If 50-Ohm mode in use, limit gain to TBD + if(afe->termination == TS_TERM_50) + { + gain_request -= afe->cal.attenuatorGain50_mdB; + } + else if(gain_request < LMH6518_MIN_GAIN_mdB) + { + // Update Attenuation if needed + afe->isAttenuated = true; + ts_afe_attenuation_control(afe, true); + gain_request -= afe->cal.attenuatorGain1M_mdB; + } } else if(gain_request < LMH6518_MIN_GAIN_mdB) { // Update Attenuation if needed - afe->isAttenuated = true; ts_afe_attenuation_control(afe, true); gain_request -= afe->cal.attenuatorGain1M_mdB; } @@ -148,7 +157,7 @@ int32_t ts_afe_set_gain(ts_afe_t* afe, int32_t gain_mdB) else { ts_afe_attenuation_control(afe, false); - if(afe->termination == TS_TERM_50) + if(isBetaDevice(afe->termPin.fd) && afe->termination == TS_TERM_50) { gain_actual += afe->cal.attenuatorGain50_mdB; } From ca32580db28eb49d2b4194c589f6d0cb589f0a78 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 30 Aug 2025 09:03:01 -0400 Subject: [PATCH 43/83] Update 12-bit mode handling. Add 12bit shuffler modes. Allow 4096 resolution setting. Set register to control data framing. Add 12bit mode to capture test. --- example/thunderscope_test.cpp | 32 ++++++++++++++++++++++---- litepcie/public_h/csr.h | 10 +++++--- litepcie/public_h/soc.h | 9 ++++---- src/adc.c | 43 ++++++++++++++++++++++++++--------- src/adc.h | 8 +++++++ src/hmcad15xx.h | 6 ++--- src/ts_channel.c | 2 +- src/ts_channel.h | 2 +- 8 files changed, 84 insertions(+), 28 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 45f235b..0f05fed 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -306,14 +306,16 @@ static void test_io(file_t fd, bool isBeta) } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, - uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip) + uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, bool is12bit) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); uint32_t bitslip_count = 0; + uint32_t dbg_monitor; uint8_t* sampleBuffer = (uint8_t*)calloc(TS_SAMPLE_BUFFER_SIZE, 0x1000); uint64_t sampleLen = 0; + uint32_t sampleRate = 1000000000; //Setup and Enable Channels tsChannelParam_t chConfig = {0}; @@ -339,6 +341,16 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ // Uncomment to use Test Pattern // thunderscopeCalibrationAdcTest(tsHdl, TS_ADC_TEST_RAMP, 0); + if(is12bit) + { + sampleRate = 600000000; + thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, 4096); + } + else + { + thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, 256); + } + printf("- Checking HMCAD1520 Sample Rate..."); litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); NS_DELAY(500000000); @@ -348,6 +360,8 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); printf("Bitslip Snapshot: %lu\r\n", bitslip_count); + dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); + printf("RANGE: 0x%08x\r\n", dbg_monitor); } @@ -381,6 +395,8 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); printf("Bitslip Snapshot: %lu\r\n", bitslip_count); + dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); + printf("RANGE: 0x%08x\r\n", dbg_monitor); } } } @@ -412,15 +428,15 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ outFile.close(); AudioFile outWav; - outWav.setBitDepth(8); + is12bit ? outWav.setBitDepth(16) : outWav.setBitDepth(8); outWav.setNumChannels(numChan); if(numChan > 2) { - outWav.setSampleRate(1000000000/4); + outWav.setSampleRate(sampleRate/4); } else { - outWav.setSampleRate(1000000000/numChan); + outWav.setSampleRate(sampleRate/numChan); } AudioFile::AudioBuffer wavBuffer; @@ -592,6 +608,7 @@ int main(int argc, char** argv) uint8_t ac_couple = 0; uint8_t term = 0; bool bitslip = false; + bool mode12bit = false; struct optparse_long argList[] = { {"dev", 'd', OPTPARSE_REQUIRED}, @@ -602,6 +619,7 @@ int main(int argc, char** argv) {"ac", 'a', OPTPARSE_NONE}, {"term", 't', OPTPARSE_NONE}, {"bits", 's', OPTPARSE_NONE}, + {"12bit", 'm', OPTPARSE_NONE}, {0} }; @@ -647,6 +665,10 @@ int main(int argc, char** argv) bitslip = true; argCount++; break; + case 'm': + mode12bit = true; + argCount++; + break; case '?': fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); print_help(); @@ -755,7 +777,7 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip); + test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit); } // Flash test else if(0 == strcmp(arg, "flash")) diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index d7ee6ab..bf8e8a5 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (c213c4274) on 2025-04-27 23:07:46 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-08-29 23:14:46 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -306,6 +306,8 @@ #define CSR_ADC_HMCAD1520_SAMPLE_COUNT_SIZE 1 #define CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR (CSR_BASE + 0x6824L) #define CSR_ADC_HMCAD1520_DATA_CHANNELS_SIZE 1 +#define CSR_ADC_HMCAD1520_SAMPLE_BITS_ADDR (CSR_BASE + 0x6828L) +#define CSR_ADC_HMCAD1520_SAMPLE_BITS_SIZE 1 /* ADC Fields */ #define CSR_ADC_CONTROL_ACQ_EN_OFFSET 0 @@ -337,9 +339,11 @@ #define CSR_ADC_HMCAD1520_RANGE_MAX23_OFFSET 24 #define CSR_ADC_HMCAD1520_RANGE_MAX23_SIZE 8 #define CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET 0 -#define CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_SIZE 2 -#define CSR_ADC_HMCAD1520_DATA_CHANNELS_RUN_LENGTH_OFFSET 2 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_SIZE 4 +#define CSR_ADC_HMCAD1520_DATA_CHANNELS_RUN_LENGTH_OFFSET 8 #define CSR_ADC_HMCAD1520_DATA_CHANNELS_RUN_LENGTH_SIZE 6 +#define CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_OFFSET 0 +#define CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_SIZE 2 /* FRONTEND Registers */ #define CSR_FRONTEND_BASE (CSR_BASE + 0x7000L) diff --git a/litepcie/public_h/soc.h b/litepcie/public_h/soc.h index 992025c..a75a0f9 100644 --- a/litepcie/public_h/soc.h +++ b/litepcie/public_h/soc.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (f63d4a833) on 2024-12-26 19:07:59 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-08-29 22:49:50 //-------------------------------------------------------------------------------- #ifndef __GENERATED_SOC_H #define __GENERATED_SOC_H @@ -10,15 +10,16 @@ #define CONFIG_CPU_FAMILY #define CONFIG_CPU_NAME #define CONFIG_CPU_HUMAN_NAME "Unknown" -#define CONFIG_IDENTIFIER "LitePCIe SoC on ThunderScope A200T (dca96645) 2024-12-26 19:07:58" +#define CONFIG_IDENTIFIER "LitePCIe SoC on ThunderScope DEV (0.2.0-next-66d756e-20250829-dirty) 2025-08-29 22:49:49" #define DMA_CHANNELS 1 #define DMA_ADDR_WIDTH 64 #define PCIE_DMA0_READER_INTERRUPT 0 #define PCIE_DMA0_WRITER_INTERRUPT 1 #define SPIFLASH_PHY_FREQUENCY 37500000 -#define SPIFLASH_MODULE_NAME "s25fl256s" -#define SPIFLASH_MODULE_TOTAL_SIZE 33554432 +#define SPIFLASH_MODULE_NAME "mx25u6435e" +#define SPIFLASH_MODULE_TOTAL_SIZE 8388608 #define SPIFLASH_MODULE_PAGE_SIZE 256 +#define SPIFLASH_MODULE_QUAD_CAPABLE #define CONFIG_CSR_DATA_WIDTH 32 #define CONFIG_CSR_ALIGNMENT 32 #define CONFIG_BUS_STANDARD "wishbone" diff --git a/src/adc.c b/src/adc.c index 5a5523b..2ef8fd5 100644 --- a/src/adc.c +++ b/src/adc.c @@ -16,11 +16,17 @@ #include "ts_calibration.h" +#define TS_ADC_DATA_FRAMING_8BIT (0) +#define TS_ADC_DATA_FRAMING_12BIT (1 << CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_OFFSET); + typedef enum adc_shuffle_e { - ADC_SHUFFLE_1CH = 0, - ADC_SHUFFLE_2CH = 1, - ADC_SHUFFLE_4CH = 2, + ADC_8B_SHUFFLE_1CH = 0, + ADC_8B_SHUFFLE_2CH = 1, + ADC_8B_SHUFFLE_4CH = 2, + ADC_12B_SHUFFLE_1CH = 3, + ADC_12B_SHUFFLE_2CH = 4, + ADC_12B_SHUFFLE_4CH = 5, } adc_shuffle_t; @@ -105,13 +111,24 @@ int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse) } int32_t ts_adc_channel_enable(ts_adc_t* adc, uint8_t channel, uint8_t enable) +{ + if(adc == NULL || channel >= TS_NUM_CHANNELS) + { + return TS_STATUS_ERROR; + } + else + { + adc->tsChannels[channel].active = enable; + return ts_adc_update_channels(adc); + } +} + +int32_t ts_adc_update_channels(ts_adc_t* adc) { int32_t retVal; uint8_t activeCount = 0; uint8_t inactiveCount = 0; - adc_shuffle_t shuffleMode = ADC_SHUFFLE_1CH; - - adc->tsChannels[channel].active = enable; + adc_shuffle_t shuffleMode = ADC_8B_SHUFFLE_1CH; for(uint8_t i=0; i < TS_NUM_CHANNELS; i++) { @@ -143,22 +160,23 @@ int32_t ts_adc_channel_enable(ts_adc_t* adc, uint8_t channel, uint8_t enable) if(activeCount == 1) { adc->adcDev.mode = HMCAD15_SINGLE_CHANNEL; - shuffleMode = ADC_SHUFFLE_1CH; + shuffleMode = ((adc->adcDev.width == HMCAD15_8_BIT) ? ADC_8B_SHUFFLE_1CH : ADC_12B_SHUFFLE_1CH); } else if(activeCount == 2) { adc->adcDev.mode = HMCAD15_DUAL_CHANNEL; - shuffleMode = ADC_SHUFFLE_2CH; + shuffleMode = ((adc->adcDev.width == HMCAD15_8_BIT) ? ADC_8B_SHUFFLE_2CH : ADC_12B_SHUFFLE_2CH); } else { adc->adcDev.mode = HMCAD15_QUAD_CHANNEL; - shuffleMode = ADC_SHUFFLE_4CH; + shuffleMode = ((adc->adcDev.width == HMCAD15_8_BIT) ? ADC_8B_SHUFFLE_4CH : ADC_12B_SHUFFLE_4CH); } retVal = hmcad15xx_set_channel_config(&adc->adcDev); litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); - litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR, shuffleMode << CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR, + shuffleMode << CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET); } return retVal; @@ -200,15 +218,18 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res return TS_STATUS_ERROR; } hmcad15xxDataWidth_t data_mode = HMCAD15_8_BIT; + uint32_t sample_bits = TS_ADC_DATA_FRAMING_8BIT; if(resolution == 4096) { data_mode = HMCAD15_12_BIT; + sample_bits = TS_ADC_DATA_FRAMING_12BIT; } //else support 14-bit precise mode? if(TS_STATUS_OK == hmcad15xx_set_sample_mode(&adc->adcDev, sample_rate, data_mode)) { - hmcad15xx_set_channel_config(&adc->adcDev); + ts_adc_update_channels(adc); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_SAMPLE_BITS_ADDR, sample_bits); litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); return TS_STATUS_OK; } diff --git a/src/adc.h b/src/adc.h index d0422d2..ef7cec6 100644 --- a/src/adc.h +++ b/src/adc.h @@ -68,6 +68,14 @@ int32_t ts_adc_set_gain(ts_adc_t* adc, uint8_t channel, int32_t gainCoarse); */ int32_t ts_adc_channel_enable(ts_adc_t* adc, uint8_t channel, uint8_t enable); +/** + * @brief Update the ADC configuration with the current active channels + * + * @param adc Pointer to a ADC instance + * @return int32_t TS_STATUS_OK if the channels were updated successfully + */ +int32_t ts_adc_update_channels(ts_adc_t* adc); + /** * @brief Shutdown the Thunderscope ADC * diff --git a/src/hmcad15xx.h b/src/hmcad15xx.h index 65428a9..c460fb2 100644 --- a/src/hmcad15xx.h +++ b/src/hmcad15xx.h @@ -166,9 +166,9 @@ typedef enum hmcad15xxMode_e typedef enum hmcad15xxDataWidth_e { - HMCAD15_8_BIT, - HMCAD15_12_BIT, - HMCAD15_14_BIT + HMCAD15_8_BIT = 0, + HMCAD15_12_BIT = 1, + HMCAD15_14_BIT = 5 } hmcad15xxDataWidth_t; typedef enum hmcad15xxPower_e diff --git a/src/ts_channel.c b/src/ts_channel.c index d478b14..2113a4d 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -539,7 +539,7 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin //TODO - Support valid rate/resolution combinations if((rate < TS_MIN_SAMPLE_RATE) || (rate > TS_MAX_SAMPLE_RATE) - || (resolution != 256)) + || ((resolution != 256) && (resolution != 4096))) { return TS_INVALID_PARAM; } diff --git a/src/ts_channel.h b/src/ts_channel.h index d479f0f..0198e5d 100644 --- a/src/ts_channel.h +++ b/src/ts_channel.h @@ -81,7 +81,7 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels); * * @param tsChannels Thunderscope Channel handle * @param rate Samples per Second - * @param resolution Number of bits in each sample. Valid values are 2^8 (256), 2^12 (4096), and 2^14 (16384). + * @param resolution Number of bits in each sample. Valid values are 2^8 (256) and 2^12 (4096). * @return int32_t TS_STATUS_OK on success, else TS_STATUS_ERROR */ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution); From 04e6ac8a43d43c43dbd9afc0f0695b0ecfc9b18f Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 11 Sep 2025 23:51:19 -0400 Subject: [PATCH 44/83] Fix sample rate handling for 8-bit and 12-bit modes. Add LVDS termination configuration in hmcad15xx initialization. --- example/thunderscope_test.cpp | 9 +++++++++ include/ts_common.h | 5 ++--- litepcie/public_h/csr.h | 28 +++++++++++++++++++++++++++- src/adc.c | 9 +++++---- src/hmcad15xx.c | 9 +++++++++ src/hmcad15xx.h | 14 ++++++++++++++ src/ts_channel.c | 31 ++++++++++++++++++++----------- 7 files changed, 86 insertions(+), 19 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 0f05fed..afc17d8 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -360,6 +360,10 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); printf("Bitslip Snapshot: %lu\r\n", bitslip_count); + dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR); + printf("FRAME Debug: 0x%08x\r\n", dbg_monitor); + dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_ADC_DEBUG_ADDR); + printf("Gearbox Valid: %04x\r\n", dbg_monitor); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); printf("RANGE: 0x%08x\r\n", dbg_monitor); } @@ -371,6 +375,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ uint64_t data_sum = 0; //Start Sample capture thunderscopeDataEnable(tsHdl, 1); + litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); auto startTime = std::chrono::steady_clock::now(); if(sampleBuffer != NULL) @@ -395,6 +400,10 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); printf("Bitslip Snapshot: %lu\r\n", bitslip_count); + dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR); + printf("FRAME Debug: 0x%08x\r\n", dbg_monitor); + dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_ADC_DEBUG_ADDR); + printf("Gearbox Valid: %04x\r\n", dbg_monitor); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); printf("RANGE: 0x%08x\r\n", dbg_monitor); } diff --git a/include/ts_common.h b/include/ts_common.h index 0b824f2..94884b6 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -25,9 +25,8 @@ extern "C" { #define TS_IDENT_STR_LEN (256) -#define TS_MAX_SAMPLE_RATE (1000000000) -#define TS_MAX_DUAL_CH_RATE (500000000) -#define TS_MAX_QUAD_CH_RATE (250000000) +#define TS_MAX_8BIT_SAMPLE_RATE (1000000000) +#define TS_MAX_12BIT_SAMPLE_RATE (640000000) #define TS_MIN_SAMPLE_RATE (15000000) #define TS_HW_ID_REV_MASK (7) diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index bf8e8a5..4f5c473 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-08-29 23:14:46 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-09-11 23:42:26 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -308,6 +308,10 @@ #define CSR_ADC_HMCAD1520_DATA_CHANNELS_SIZE 1 #define CSR_ADC_HMCAD1520_SAMPLE_BITS_ADDR (CSR_BASE + 0x6828L) #define CSR_ADC_HMCAD1520_SAMPLE_BITS_SIZE 1 +#define CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR (CSR_BASE + 0x682cL) +#define CSR_ADC_HMCAD1520_FRAME_DEBUG_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_ADDR (CSR_BASE + 0x6830L) +#define CSR_ADC_HMCAD1520_ADC_DEBUG_SIZE 1 /* ADC Fields */ #define CSR_ADC_CONTROL_ACQ_EN_OFFSET 0 @@ -330,6 +334,8 @@ #define CSR_ADC_HMCAD1520_CONTROL_DELAY_INC_SIZE 1 #define CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET 3 #define CSR_ADC_HMCAD1520_CONTROL_STAT_RST_SIZE 1 +#define CSR_ADC_HMCAD1520_CONTROL_DATA_DELAY_INC_OFFSET 4 +#define CSR_ADC_HMCAD1520_CONTROL_DATA_DELAY_INC_SIZE 1 #define CSR_ADC_HMCAD1520_RANGE_MIN01_OFFSET 0 #define CSR_ADC_HMCAD1520_RANGE_MIN01_SIZE 8 #define CSR_ADC_HMCAD1520_RANGE_MAX01_OFFSET 8 @@ -344,6 +350,26 @@ #define CSR_ADC_HMCAD1520_DATA_CHANNELS_RUN_LENGTH_SIZE 6 #define CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_OFFSET 0 #define CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_SIZE 2 +#define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_CLK_OFFSET 0 +#define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_CLK_SIZE 8 +#define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_VALID_OFFSET 8 +#define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_0_VALID_OFFSET 0 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_0_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_1_VALID_OFFSET 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_1_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_2_VALID_OFFSET 2 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_2_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_3_VALID_OFFSET 3 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_3_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_4_VALID_OFFSET 4 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_4_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_5_VALID_OFFSET 5 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_5_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_6_VALID_OFFSET 6 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_6_VALID_SIZE 1 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_7_VALID_OFFSET 7 +#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_7_VALID_SIZE 1 /* FRONTEND Registers */ #define CSR_FRONTEND_BASE (CSR_BASE + 0x7000L) diff --git a/src/adc.c b/src/adc.c index 2ef8fd5..240089f 100644 --- a/src/adc.c +++ b/src/adc.c @@ -172,11 +172,12 @@ int32_t ts_adc_update_channels(ts_adc_t* adc) adc->adcDev.mode = HMCAD15_QUAD_CHANNEL; shuffleMode = ((adc->adcDev.width == HMCAD15_8_BIT) ? ADC_8B_SHUFFLE_4CH : ADC_12B_SHUFFLE_4CH); } - retVal = hmcad15xx_set_channel_config(&adc->adcDev); - + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR, shuffleMode << CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET); + + retVal = hmcad15xx_set_channel_config(&adc->adcDev); } return retVal; @@ -228,9 +229,9 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res if(TS_STATUS_OK == hmcad15xx_set_sample_mode(&adc->adcDev, sample_rate, data_mode)) { - ts_adc_update_channels(adc); litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_SAMPLE_BITS_ADDR, sample_bits); - litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); + + ts_adc_update_channels(adc); return TS_STATUS_OK; } else diff --git a/src/hmcad15xx.c b/src/hmcad15xx.c index 42a8fd9..587c71b 100644 --- a/src/hmcad15xx.c +++ b/src/hmcad15xx.c @@ -42,6 +42,7 @@ int32_t hmcad15xx_init(hmcad15xxADC_t* adc, spi_dev_t dev) adc->clockDiv = HMCAD15_CLK_DIV_1; adc->fullScale_x10 = HMCAD15_FULL_SCALE_DEFAULT; adc->drive = HMCAD15_LVDS_DS_15; + adc->lvdsTerm = HMCAD15_LVDS_TERM_94; adc->lvdsPhase = HMCAD15_LVDS_PHASE_DEFAULT; adc->low_clk = 0; @@ -296,6 +297,14 @@ static void hmcad15xxApplyLvdsMode(hmcad15xxADC_t* adc) HMCAD15_LVDS_DS_DATA(adc->drive)); hmcad15xxRegWrite(adc, HMCAD15_REG_LVDS_CURRENT, data); + // Set LVDS Termination + data = adc->lvdsTerm == 0 ? 0 : + (HMCAD15_LVDS_DS_LCLK(adc->lvdsTerm) | + HMCAD15_LVDS_DS_FRAME(adc->lvdsTerm) | + HMCAD15_LVDS_DS_DATA(adc->lvdsTerm) | + HMCAD15_LVDS_TERM_EN_MASK); + hmcad15xxRegWrite(adc, HMCAD15_REG_TERM, data); + // Set LVDS Data Width (bits per sample) data = (HMCAD15_DATA_WIDTH(adc->width) | HMCAD15_LOW_CLK(adc->low_clk)); diff --git a/src/hmcad15xx.h b/src/hmcad15xx.h index c460fb2..b66f334 100644 --- a/src/hmcad15xx.h +++ b/src/hmcad15xx.h @@ -108,6 +108,11 @@ extern "C" { #define HMCAD15_LVDS_DS_FRAME(x) (((x) & 0x07) << 4) #define HMCAD15_LVDS_DS_DATA(x) (((x) & 0x07) << 8) +#define HMCAD15_LVDS_TERM_LCLK(x) ((x) & 0x07) +#define HMCAD15_LVDS_TERM_FRAME(x) (((x) & 0x07) << 4) +#define HMCAD15_LVDS_TERM_DATA(x) (((x) & 0x07) << 8) +#define HMCAD15_LVDS_TERM_EN_MASK (1 << 14) + #define HMCAD15_DATA_WIDTH(x) ((x) & 0x07) #define HMCAD15_LOW_CLK(x) (((x) & 0x01) << 3) @@ -147,6 +152,14 @@ extern "C" { #define HMCAD15_LVDS_DS_55 (6) #define HMCAD15_LVDS_DS_45 (7) +#define HMCAD15_LVDS_TERM_DEFAULT (0) +#define HMCAD15_LVDS_TERM_260 (1) +#define HMCAD15_LVDS_TERM_150 (2) +#define HMCAD15_LVDS_TERM_94 (3) +#define HMCAD15_LVDS_TERM_125 (4) +#define HMCAD15_LVDS_TERM_80 (5) +#define HMCAD15_LVDS_TERM_66 (6) +#define HMCAD15_LVDS_TERM_55 (7) #define HMCAD15_CLK_DIV_DEFAULT (1) #define HMCAD15_LVDS_PHASE_DEFAULT (HMCAD15_LVDS_PHASE_0DEG) @@ -214,6 +227,7 @@ typedef struct hmcad15xxADC_s uint8_t clockDiv; uint8_t lvdsPhase; uint8_t drive; + uint8_t lvdsTerm; uint8_t low_clk; } hmcad15xxADC_t; diff --git a/src/ts_channel.c b/src/ts_channel.c index 2113a4d..e0ff89c 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -536,34 +536,43 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin } ts_channel_t* ts = (ts_channel_t*)tsChannels; uint64_t actual_rate = 0; + uint64_t max_rate = 0; - //TODO - Support valid rate/resolution combinations - if((rate < TS_MIN_SAMPLE_RATE) || (rate > TS_MAX_SAMPLE_RATE) - || ((resolution != 256) && (resolution != 4096))) + if(resolution == 256) + { + max_rate = TS_MAX_8BIT_SAMPLE_RATE; + } + else if(resolution == 4096) + { + max_rate = TS_MAX_12BIT_SAMPLE_RATE; + } + else + { + return TS_INVALID_PARAM; + } + + if((rate < TS_MIN_SAMPLE_RATE) || (rate > max_rate)) { return TS_INVALID_PARAM; } - //Input validation - if(ts->adc.adcDev.mode == HMCAD15_SINGLE_CHANNEL) + if(ts->adc.adcDev.mode == HMCAD15_SINGLE_CHANNEL) { actual_rate = rate; } else if(ts->adc.adcDev.mode == HMCAD15_DUAL_CHANNEL) { - //Limit upper rate - if(rate > TS_MAX_DUAL_CH_RATE) + if(rate > (max_rate/2)) { - rate = TS_MAX_DUAL_CH_RATE; + rate = (max_rate/2); } actual_rate = rate * 2; } else { - //Limit upper rate - if(rate > TS_MAX_QUAD_CH_RATE) + if(rate > (max_rate/4)) { - rate = TS_MAX_QUAD_CH_RATE; + rate = (max_rate/4); } actual_rate = rate * 4; } From 99176020b3f3dc94f35aec4e5cfdc0bc5a376da9 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 20 Sep 2025 17:29:14 -0400 Subject: [PATCH 45/83] Fix Manual AFE control function to take a struct handle. Swap tsChannelCtrl_t parameters to eliminate hole in struct. --- bindings/python/ts_calibration.pxd | 4 ++-- bindings/python/tslitex.pyx | 2 +- example/thunderscope_cal.cpp | 6 +++--- include/ts_calibration.h | 10 +++++----- src/thunderscope.c | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bindings/python/ts_calibration.pxd b/bindings/python/ts_calibration.pxd index 2ce2fc0..0e0905d 100644 --- a/bindings/python/ts_calibration.pxd +++ b/bindings/python/ts_calibration.pxd @@ -38,8 +38,8 @@ cdef extern from "ts_calibration.h": uint8_t atten uint8_t term uint8_t dc_couple - uint16_t dac uint8_t dpot + uint16_t dac uint8_t pga_high_gain uint8_t pga_atten uint8_t pga_bw @@ -60,4 +60,4 @@ cdef extern from "ts_calibration.h": int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t* cal) - int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl) + int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t* ctrl) diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index 1e7f916..0b49f63 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -138,7 +138,7 @@ cdef class Channel: def ManualCtrl(self, params: ts_calibration.tsChannelCtrl_t): cdef int32_t retVal - retVal = ts_calibration.thunderscopeCalibrationManualCtrl(self.dev, self._channel, params) + retVal = ts_calibration.thunderscopeCalibrationManualCtrl(self.dev, self._channel, ¶ms) if retVal != tslitex.TS_STATUS_OK: raise ValueError(f"Failed to manually set Channel {self._channel} Parameters {params}") diff --git a/example/thunderscope_cal.cpp b/example/thunderscope_cal.cpp index bec54e2..afbb661 100644 --- a/example/thunderscope_cal.cpp +++ b/example/thunderscope_cal.cpp @@ -274,7 +274,7 @@ static void cal_step_2(tsHandle_t pTs, uint8_t chanBitmap) { if((chanBitmap >> i) & 0x1) { - thunderscopeCalibrationManualCtrl(pTs, i, afe_ctrl); + thunderscopeCalibrationManualCtrl(pTs, i, &afe_ctrl); } } @@ -313,7 +313,7 @@ static void cal_step_2(tsHandle_t pTs, uint8_t chanBitmap) { if((chanBitmap >> i) & 0x1) { - thunderscopeCalibrationManualCtrl(pTs, i, afe_ctrl); + thunderscopeCalibrationManualCtrl(pTs, i, &afe_ctrl); } } @@ -372,7 +372,7 @@ static void cal_step_3(tsHandle_t pTs, uint8_t chanBitmap) afe_ctrl.dac = (uint16_t)(calibration.afeCal[i].bias_uV); // afe_ctrl.dac = (int32_t)(calibration.afeCal[i].bias_mV * 4095 / 5); afe_ctrl.dpot = ((((500) * calibration.afeCal[i].trimRheostat_range) / MCP4432_MAX) + MCP4432_RWIPER); - thunderscopeCalibrationManualCtrl(pTs, i, afe_ctrl); + thunderscopeCalibrationManualCtrl(pTs, i, &afe_ctrl); } } diff --git a/include/ts_calibration.h b/include/ts_calibration.h index a66353a..2e41bff 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -58,10 +58,10 @@ typedef struct tsChannelCtrl_s uint8_t term; // DC Coupling uint8_t dc_couple; - // Trim DAC - uint16_t dac; // Trim DPOT uint8_t dpot; + // Trim DAC + uint16_t dac; //Preamp Control uint8_t pga_high_gain; uint8_t pga_atten; @@ -108,7 +108,7 @@ int32_t thunderscopeAdcCalibrationSet(tsHandle_t ts, tsAdcCalibration_t *cal); * @brief Get the calibration data for the ADC on the Thunderscope device * * @param ts Handle to the Thunderscope device - * @param cal ADC Calibration data Pointer + * @param cal ADC Calibration Data Pointer * @return int32_t TS_STATUS_OK if the calibration was retrieved */ int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t *cal); @@ -118,10 +118,10 @@ int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t *cal); * * @param ts Handle to the Thunderscope device * @param channel Channel number - * @param ctrl AFE Control parameters + * @param ctrl AFE Control Parameters Pointer * @return int32_t TS_STATUS_OK if the parameters were applied */ -int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl); +int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t *ctrl); /** * @brief Manually set the ADC test pattern mode. diff --git a/src/thunderscope.c b/src/thunderscope.c index c214b6a..0a56d37 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -276,13 +276,13 @@ int32_t thunderscopeAdcCalibrationGet(tsHandle_t ts, tsAdcCalibration_t *cal) return TS_STATUS_ERROR; } -int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t ctrl) +int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsChannelCtrl_t *ctrl) { ts_inst_t* pInst = (ts_inst_t*)ts; if(pInst && pInst->initialized) { - return ts_channel_calibration_manual(pInst->pChannel, channel, ctrl); + return ts_channel_calibration_manual(pInst->pChannel, channel, *ctrl); } return TS_STATUS_ERROR; From 13b8bc6e6582c9e9ac9bc99d9f84964b240adf1b Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 23 Sep 2025 20:42:06 -0400 Subject: [PATCH 46/83] Add API to read/write the User data section of flash - Support Reading starting at an arbitrary offset within the user data region. - Support Writing to the user data region at an arbitrary offset. - Data reads are limited to 32-bit word-aligned offsets and lengths - TODO: Allow byte resolution on reads - TODO: Add write protection to factory partitions - TODO: Improve Erase function to allow 4K sectors --- bindings/python/tslitex.pxd | 2 +- example/thunderscope_fw.cpp | 75 ++++++++++++--- include/thunderscope.h | 29 +++++- src/spiflash.c | 5 +- src/spiflash.h | 4 + src/thunderscope.c | 27 +++++- src/ts_fw_manager.c | 184 +++++++++++++++++++++++++++++++++++- src/ts_fw_manager.h | 19 +--- 8 files changed, 307 insertions(+), 38 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 647f715..ac46c97 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -94,6 +94,6 @@ cdef extern from "thunderscope.h": int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) nogil - int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) nogil + int32_t thunderscopeFwUpdate(tsHandle_t ts, const char* bitstream, uint32_t len) nogil int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 1ae779b..9fa14af 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -23,15 +23,62 @@ #include +#define MAX_USER_DATA_SIZE (0x500000) -static void cal_save(tsHandle_t ts, const char* file_path) +static void user_write(tsHandle_t ts, const char* file_path, uint32_t offset) { - + auto file_size = std::filesystem::file_size(file_path); + int32_t status; + // Open data file + std::ifstream file(file_path, std::ios::binary); + if(file) + { + char* bitstream = new char[file_size]; + file.read(bitstream, file_size); + + printf("User Data write in progress...."); + + // Load New bitstream + status = thunderscopeUserDataWrite(ts, bitstream, offset, (uint32_t)file_size); + + printf("Data Write %dB Complete!\r\n", status); + + // Close File + file.close(); + delete bitstream; + } + else + { + printf("ERROR User Data Write: Failed to open file %s\r\n", file_path); + } } -static void cal_load(tsHandle_t ts, const char* file_path) +static void user_read(tsHandle_t ts, const char* file_path) { - + int32_t read_len = 0; + // Open file to store data + std::ofstream file(file_path, std::ios::binary); + if(file) + { + char* data_buffer = new char[MAX_USER_DATA_SIZE]; + + printf("Reading User Data ...."); + + // Get data from TS + read_len = thunderscopeUserDataRead(ts, data_buffer, 0, MAX_USER_DATA_SIZE); + file.write(data_buffer, read_len); + + printf("Read %dB Complete!\r\n", read_len); + + // Close File + file.flush(); + file.close(); + delete data_buffer; + } + else + { + printf("ERROR User Data Read: Failed to open file %s\r\n", file_path); + } } static void fw_upgrade(tsHandle_t ts, const char* file_path) @@ -70,10 +117,10 @@ static void fw_restore(tsHandle_t ts) static void print_help(void) { printf("TS FW Update Util Usage:\r\n"); - printf("\tcal_save [dest_file] - Save the current calibration to an XML file\r\n"); + printf("\tuser_read [dest_file] - Save user data to a file\r\n"); printf("\t\t[dest_file] - Name of the file to save\r\n"); - printf("\tcal_load [cal_file] - Load user calibration data\r\n"); - printf("\t\t[cal_file] - Name of the XML Calibration file to load\r\n"); + printf("\tuser_write [data_file] - Load user data to the Thunderscope\r\n"); + printf("\t\t[data_file] - Name of the XML Calibration file to load\r\n"); printf("\tfw_upgrade [bitstream_file] - Load a new Bitstream\r\n"); printf("\t\t[bitstream_file] - Name of the Gateware Bitstream file to load\r\n"); printf("\tfactory_restore - Restores the factory bitstream to the primary location\r\n"); @@ -92,9 +139,11 @@ int main(int argc, char** argv) tsHandle_t ts; tsDeviceInfo_t infos; tsScopeState_t status; + uint32_t offs = 0; struct optparse_long argList[] = { {"dev", 'd', OPTPARSE_REQUIRED}, + {"offs", 'o', OPTPARSE_REQUIRED}, {0} }; @@ -112,6 +161,10 @@ int main(int argc, char** argv) idx = strtol(options.optarg, NULL, 0); argCount+=2; break; + case 'o': + offs = strtol(options.optarg, NULL, 0); + argCount += 2; + break; case '?': fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); print_help(); @@ -176,14 +229,14 @@ int main(int argc, char** argv) { file_path = argv[argCount]; - if(0 == strcmp(arg, "cal_save")) + if(0 == strcmp(arg, "user_write")) { - cal_save(ts, file_path); + user_write(ts, file_path, offs); } // Setup Channel, record samples to buffer, save buffer to file - else if(0 == strcmp(arg, "cal_load")) + else if(0 == strcmp(arg, "user_read")) { - cal_load(ts, file_path); + user_read(ts, file_path); } else if(0 == strcmp(arg, "fw_upgrade")) { diff --git a/include/thunderscope.h b/include/thunderscope.h index e80e572..abeb5fe 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -113,7 +113,34 @@ int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len); * @param len Length of the data buffer available * @return int32_t TS_STATUS_OK if the firmware was updated successfully, or a negative error code */ -int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len); +int32_t thunderscopeFwUpdate(tsHandle_t ts, const char* bitstream, uint32_t len); + +/** + * @brief Read user-defined data blob from the Thunderscope + * + * @param ts Handle to the Thunderscope device + * @param buffer Pointer to a buffer to store the data as read. + * @param offset Offset to begin reading within the user data section + * @param readLen Length of the data buffer + * @return int32_t Length of the data read if successfull, or a negative error code + */ +int32_t thunderscopeUserDataRead(tsHandle_t ts, char* buffer, uint32_t offset, uint32_t readLen); + +/** + * @brief Write a user-defined data blob to the Thunderscope + * + * This function writes from a section of SPI Flash reserved for application-defined data, such as + * calibration data, persistent settings, etc. Because the SPI flash needs to erase on a 4k page, + * any offset or length not aligned with a 4k boundary will be read to a temporary buffer, modified, + * and written back. + * + * @param ts Handle to the Thunderscope device + * @param buffer Pointer to a buffer of data to be stored. + * @param offset Offset to begin writing within the user data section + * @param writeLen Length of the data buffer + * @return int32_t Length of the data written if successfull, or a negative error code + */ +int32_t thunderscopeUserDataWrite(tsHandle_t ts, const char* buffer, uint32_t offset, uint32_t writeLen); /** * @brief Get the current progress of the firmware update diff --git a/src/spiflash.c b/src/spiflash.c index b5a4126..e9dc107 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -17,9 +17,6 @@ #include #include -#define SPI_FLASH_PROG_SIZE 256 -#define SPI_FLASH_ERASE_SIZE (64*1024) - #define SPI_FLASH_WINDOW_SIZE 0x10000 #define SPI_FLASH_CLK_DIV_DEFAULT (1) @@ -244,7 +241,7 @@ int32_t spiflash_erase(spiflash_dev_t* dev, uint32_t addr, uint32_t len) uint32_t i = 0; uint32_t j = 0; //Check Address is aligned to the erase sector - if(addr & 0xffff) + if((addr % SPI_FLASH_ERASE_SIZE) != 0) { LOG_ERROR("Error: Flash Erase address must be 64K-aligned (0x%08X)", addr); return TS_STATUS_ERROR; diff --git a/src/spiflash.h b/src/spiflash.h index 5b02ca1..1636685 100644 --- a/src/spiflash.h +++ b/src/spiflash.h @@ -18,6 +18,10 @@ extern "C" { #include "ts_common.h" #include "liblitepcie.h" + +#define SPI_FLASH_PROG_SIZE 256 +#define SPI_FLASH_ERASE_SIZE (64*1024) + typedef void (*spiflash_progress_cb_t)(void* ctx, uint32_t work_done, uint32_t work_total); typedef struct spiflash_ops_s { diff --git a/src/thunderscope.c b/src/thunderscope.c index 0a56d37..ad4ae11 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -343,7 +343,7 @@ int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) } -int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) +int32_t thunderscopeFwUpdate(tsHandle_t ts, const char* bitstream, uint32_t len) { ts_inst_t* pInst = (ts_inst_t*)ts; int32_t status = TS_STATUS_ERROR; @@ -360,9 +360,32 @@ int32_t thunderscopeFwUpdate(tsHandle_t ts, char* bitstream, uint32_t len) } } - return TS_STATUS_ERROR; + return status; +} + +int32_t thunderscopeUserDataRead(tsHandle_t ts, char* buffer, uint32_t offset, uint32_t readLen) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t status = TS_STATUS_ERROR; + if(pInst) + { + status = ts_fw_manager_user_data_read(&pInst->fw, buffer, offset, readLen); + } + + return status; } +int32_t thunderscopeUserDataWrite(tsHandle_t ts, const char* buffer, uint32_t offset, uint32_t writeLen) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t status = TS_STATUS_ERROR; + if(pInst) + { + status = ts_fw_manager_user_data_write(&pInst->fw, buffer, offset, writeLen); + } + + return status; +} int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) { diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index c0e7201..026add9 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -14,6 +14,8 @@ #include "liblitepcie.h" #include "util.h" +#define FLASH_PAGE_LEN (SPI_FLASH_ERASE_SIZE) + // From AMD UG470 #define IDCODE_7A200T (0x03636093) #define IDCODE_7A100T (0x03631093) @@ -270,20 +272,194 @@ int32_t ts_fw_manager_get_progress(ts_fw_manager_t* mngr, uint32_t* progress) return TS_STATUS_OK; } -int32_t ts_fw_manager_user_cal_get(ts_fw_manager_t* mngr, const char* file_stream, uint32_t max_len) +int32_t ts_fw_manager_user_data_read(ts_fw_manager_t* mngr, char* buffer, uint32_t offset, uint32_t max_len) { + if(mngr == NULL) + { + LOG_ERROR("Invalid manager handle"); + return TS_STATUS_ERROR; + } + + uint32_t max_offset = mngr->partition_table->user_config_end - mngr->partition_table->user_config_start; + uint32_t readLen = max_len; + atomic_store(&mngr->fw_progress, 0); + mngr->fw_progress_max = max_len; + + if(buffer == NULL) + { + LOG_ERROR("User Data Read Invalid Buffer Address"); + return TS_STATUS_ERROR; + } + if (offset > max_offset) + { + LOG_ERROR("User Data Read Invalid Offset (%d is beyond range of %d)", offset, max_offset); + return TS_STATUS_ERROR; + } + if(max_len == 0) + { + LOG_ERROR("User Data Read Invalid Len (%d)", max_len); + return TS_STATUS_ERROR; + } + + if((offset + max_len) > (max_offset)) + { + readLen = max_offset - offset; + } + + //TODO Support unaligned reads + // Read File from SPI Flash + if(readLen != spiflash_read(&mngr->flash_dev, (mngr->partition_table->user_config_start + offset), buffer, readLen)) + { + LOG_ERROR("Failed to read user data partition (%d)", readLen); + return TS_STATUS_ERROR; + } - return TS_STATUS_OK; + return readLen; } -int32_t ts_fw_manager_user_cal_update(ts_fw_manager_t* mngr, const char* file_stream, uint32_t len) +int32_t ts_fw_manager_user_data_write(ts_fw_manager_t* mngr, const char* buffer, uint32_t offset, uint32_t len) { - // Verify File Good + if(mngr == NULL) + { + LOG_ERROR("Invalid manager handle"); + return TS_STATUS_ERROR; + } + + int32_t status; + uint8_t start_buffer[FLASH_PAGE_LEN]; + uint8_t end_buffer[FLASH_PAGE_LEN]; + uint32_t max_offset = mngr->partition_table->user_config_end - mngr->partition_table->user_config_start; + uint32_t start_addr = mngr->partition_table->user_config_start + (offset & ~(FLASH_PAGE_LEN - 1)); + uint32_t op_len = ((len + (offset % FLASH_PAGE_LEN) //Add prepend page len + + (FLASH_PAGE_LEN - 1)) / FLASH_PAGE_LEN) //Round up to total number of pages + * FLASH_PAGE_LEN; // Multiply to get number of bytes + + atomic_store(&mngr->fw_progress, 0); + mngr->fw_progress_max = op_len*2; + + + // Verify Parameters Good + if(buffer == NULL) + { + LOG_ERROR("User Data Write Invalid Buffer Address"); + return TS_STATUS_ERROR; + } + if (offset >= max_offset) + { + LOG_ERROR("User Data Write Invalid Offset (%d is beyond range of %d)", offset, max_offset); + return TS_STATUS_ERROR; + } + if(len == 0) + { + LOG_ERROR("User Data Write Invalid Len (%d)", len); + return TS_STATUS_ERROR; + } + else if((len + offset) > max_offset) + { + LOG_ERROR("User Data Write Invalid Offset 0x%06X Len %d exceeds end of User Data Region (0x%06X)", offset, len, max_offset); + return TS_STATUS_ERROR; + } + + //Save initial page if offset not 4k-aligned + if((offset % FLASH_PAGE_LEN) != 0) + { + LOG_DEBUG("Read %d bytes at address %08X", (FLASH_PAGE_LEN), (start_addr + op_len - FLASH_PAGE_LEN)); + status = spiflash_read(&mngr->flash_dev, start_addr, start_buffer, FLASH_PAGE_LEN); + if(status != FLASH_PAGE_LEN) + { + LOG_ERROR("Failed to read SPI Flash page 0x%06X (%d)", start_addr, status); + return status; + } + } + + //Save final page + LOG_DEBUG("Read %d bytes at address %08X", (FLASH_PAGE_LEN), (start_addr + op_len - FLASH_PAGE_LEN)); + status = spiflash_read(&mngr->flash_dev, (start_addr + op_len - FLASH_PAGE_LEN), end_buffer, FLASH_PAGE_LEN); + if(status != FLASH_PAGE_LEN) + { + LOG_ERROR("Failed to read SPI Flash page 0x%06X (%d)", (start_addr + op_len - FLASH_PAGE_LEN), status); + return status; + } + // Erase User Flash Partition + LOG_DEBUG("Erase %d bytes at address %08X", (op_len), (start_addr)); + if(TS_STATUS_OK != spiflash_erase(&mngr->flash_dev, start_addr, op_len)) + { + LOG_ERROR("Failed to erase user data partition (0x%06X 0x%06X)", start_addr, op_len); + return TS_STATUS_ERROR; + } + + atomic_store(&mngr->fw_progress, op_len); // Program New User Data + if((offset % FLASH_PAGE_LEN) != 0) + { + //Copy beginning of user data over saved first page + uint32_t start_page_len = ((offset % FLASH_PAGE_LEN) + len) > FLASH_PAGE_LEN ? + (FLASH_PAGE_LEN - (offset % FLASH_PAGE_LEN)) : + len; + memcpy(&start_buffer[offset % FLASH_PAGE_LEN], buffer, start_page_len); + LOG_DEBUG("Copy %d bytes at offset %08X to start buffer", start_page_len, (offset % FLASH_PAGE_LEN)); + + LOG_DEBUG("Write %d bytes at address %08X", FLASH_PAGE_LEN, (start_addr)); + if(FLASH_PAGE_LEN != spiflash_write(&mngr->flash_dev, start_addr, start_buffer, FLASH_PAGE_LEN)) + { + LOG_ERROR("Failed to erase user bitstream partition"); + return TS_STATUS_ERROR; + } + + start_addr += FLASH_PAGE_LEN; + op_len -= FLASH_PAGE_LEN; + } + + if(op_len > FLASH_PAGE_LEN) + { + if(start_addr + op_len > + mngr->partition_table->user_config_end) + { + LOG_ERROR("Failed to write %d bytes at 0x%06X", op_len, start_addr); + return TS_STATUS_ERROR; + } + + LOG_DEBUG("Write %d bytes at address %08X", (op_len - FLASH_PAGE_LEN), (start_addr)); + if(TS_STATUS_ERROR == spiflash_write(&mngr->flash_dev, (start_addr), (uint8_t*)buffer, + (op_len - FLASH_PAGE_LEN))) + { + LOG_ERROR("Failed to write user bitstream partition"); + return TS_STATUS_ERROR; + } + start_addr += op_len - FLASH_PAGE_LEN; + op_len = FLASH_PAGE_LEN; + } + + if(op_len != 0) + { + //Copy end of user data over saved final page + uint32_t remainder = (len+offset) % FLASH_PAGE_LEN; + if(remainder == 0) + { + remainder = FLASH_PAGE_LEN; + } + memcpy(end_buffer, &buffer[len - remainder], remainder); + LOG_DEBUG("Copy %d bytes at src offset %08X to final buffer", (remainder), (len - remainder)); + + if(start_addr + FLASH_PAGE_LEN > mngr->partition_table->user_config_end) + { + LOG_ERROR("Failed to write final page at 0x%06X", start_addr + FLASH_PAGE_LEN); + return TS_STATUS_ERROR; + } + + LOG_DEBUG("Write %d bytes at address %08X", (FLASH_PAGE_LEN), (start_addr)); + if(FLASH_PAGE_LEN != spiflash_write(&mngr->flash_dev, start_addr, end_buffer, FLASH_PAGE_LEN)) + { + LOG_ERROR("Failed to erase user bitstream partition"); + return TS_STATUS_ERROR; + } + } + + atomic_store(&mngr->fw_progress, mngr->fw_progress_max); return TS_STATUS_OK; } diff --git a/src/ts_fw_manager.h b/src/ts_fw_manager.h index f182de1..6965ee4 100644 --- a/src/ts_fw_manager.h +++ b/src/ts_fw_manager.h @@ -56,38 +56,27 @@ int32_t ts_fw_manager_user_fw_update(ts_fw_manager_t* mngr, const char* file_str */ int32_t ts_fw_manager_get_progress(ts_fw_manager_t* mngr, uint32_t* progress); -/** - * @brief Read the User Calibration data from flash - * - * @param mngr Pointer to a manager instance - * @param file_stream Pointer to a buffer to store the User Calibration data - * @param len Available space in the calibration data buffer - * - * @return TS_STATUS_OK on success. - */ -int32_t ts_fw_manager_user_cal_get(ts_fw_manager_t* mngr, const char* file_stream, uint32_t max_len); - /** * @brief Load User Calibration data to flash * * @param mngr Pointer to a manager instance - * @param file_stream Pointer to a buffer containing User calibration data + * @param buffer Pointer to a buffer containing User data * @param len Length of the calibration data buffer * * @return TS_STATUS_OK on success. */ -int32_t ts_fw_manager_user_cal_update(ts_fw_manager_t* mngr, const char* file_stream, uint32_t len); +int32_t ts_fw_manager_user_data_write(ts_fw_manager_t* mngr, const char* buffer, uint32_t offset, uint32_t len); /** * @brief Save the Factory calibration data to a file * * @param mngr Pointer to a manager instance - * @param file_stream Pointer to a buffer to store the factory calibration + * @param buffer Pointer to a buffer to store the user data * @param max_len Available space in the buffer * * @return TS_STATUS_OK on success. */ -int32_t ts_fw_manager_factory_cal_get(ts_fw_manager_t* mngr, const char* file_stream, uint32_t max_len); +int32_t ts_fw_manager_user_data_read(ts_fw_manager_t* mngr, char* buffer, uint32_t offset, uint32_t max_len); /** * @brief Reset the user flash partitions with the Factory image From e348f1110cf557b5e06683a91ede0f5c463b2e2f Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 24 Sep 2025 23:48:17 -0400 Subject: [PATCH 47/83] Added arbitrary location/length spiflash reads. Added User read/write to Python bindings --- bindings/python/tslitex.pxd | 4 ++++ bindings/python/tslitex.pyx | 20 ++++++++++++++++ src/platform.c | 1 + src/spiflash.c | 46 +++++++++++++++++++++++++++++++++---- src/ts_fw_manager.c | 2 -- 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index ac46c97..fd53389 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -96,4 +96,8 @@ cdef extern from "thunderscope.h": int32_t thunderscopeFwUpdate(tsHandle_t ts, const char* bitstream, uint32_t len) nogil + int32_t thunderscopeUserDataRead(tsHandle_t ts, char* buffer, uint32_t offset, uint32_t readLen) nogil + + int32_t thunderscopeUserDataWrite(tsHandle_t ts, const char* buffer, uint32_t offset, uint32_t writeLen) nogil + int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index 0b49f63..bff09cb 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -211,6 +211,26 @@ cdef class Thunderscope: status = tslitex.thunderscopeFwUpdate(self._tsHandle, pFile, file_len) return status + def userDataRead(self, datafile not None, datalen: int, offset: int): + cdef uint32_t max_len = datalen + cdef char* pFile = datafile + cdef int32_t status + cdef uint32_t user_offset = offset + with nogil: + status = tslitex.thunderscopeUserDataRead(self._tsHandle, pFile, user_offset, max_len) + return status + + def userDataWrite(self, datafile, offset:int): + if type(datafile) is not bytes: + raise TypeError(f"bitfile arg must be 'bytes' type") + cdef uint32_t file_len = len(datafile) + cdef char* pFile = datafile + cdef uint32_t offs = offset + cdef int32_t status + with nogil: + status = tslitex.thunderscopeUserDataWrite(self._tsHandle, pFile, offs, file_len) + return status + @property def firmwareProgress(self): cdef uint32_t progress diff --git a/src/platform.c b/src/platform.c index a937be0..9f6ec88 100644 --- a/src/platform.c +++ b/src/platform.c @@ -138,6 +138,7 @@ const flash_layout_t ts_64Mb_layout = { // | 0x1000000 - 0x1AFFFFF | Primary Bitstream | // | 0x1B00000 - 0x1B0FFFF | Barrier Image B | // | 0x1B10000 - 0x1FFFFFF | Available for User Data | + const flash_layout_t ts_256Mb_layout = { .factory_bitstream_start = 0x0000000, .factory_bitstream_end = 0x0B00000, diff --git a/src/spiflash.c b/src/spiflash.c index e9dc107..3637d70 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -392,13 +392,51 @@ int32_t spiflash_init(file_t fd, spiflash_dev_t* dev) int32_t spiflash_read(spiflash_dev_t* dev, uint32_t addr, uint8_t* pData, uint32_t len) { + uint32_t read_len = 0; + uint32_t remaining = len; + uint32_t tempAddr = (addr/4)*4; //Round address down + uint8_t flashBytes[4]; //Validate Address and Lengths - if((len % 4) || (addr % 4)) + if(len == 0) { - //Only supports word-reads currently + LOG_ERROR("Failed to read with length 0"); return TS_STATUS_ERROR; } - int32_t read_len = get_flash_data(dev->fd, addr, pData, len); + + if(addr % 4 != 0) + { + read_len = get_flash_data(dev->fd, tempAddr, flashBytes, 4); + if( 4 != read_len ) + { + LOG_ERROR("Failed to read with length 0"); + return TS_STATUS_ERROR; + } + read_len = (4 - (addr%4)); + memcpy(pData, &flashBytes[addr%4], read_len); + addr += read_len; + pData += read_len; + remaining -= read_len; + } + + read_len = get_flash_data(dev->fd, addr, pData, (remaining/4)*4); + addr += read_len; + pData += read_len; + remaining -= read_len; + + if(remaining >= 4) + { + LOG_ERROR("SPIFLASH READ FAILED (%d - %d)", read_len, remaining); + } + else if(0 != remaining) + { + //Should only be 1-3 bytes left + if( 4 != get_flash_data(dev->fd, addr, flashBytes, 4)) + { + LOG_ERROR("Failed to read end of data (%d / %d)", read_len, len); + return TS_STATUS_ERROR; + } + memcpy(pData, flashBytes, remaining); + } - return len; + return (int32_t)len; } diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index 026add9..472af88 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -306,8 +306,6 @@ int32_t ts_fw_manager_user_data_read(ts_fw_manager_t* mngr, char* buffer, uint32 readLen = max_offset - offset; } - //TODO Support unaligned reads - // Read File from SPI Flash if(readLen != spiflash_read(&mngr->flash_dev, (mngr->partition_table->user_config_start + offset), buffer, readLen)) { From baa43816906f4c8391c342de8e19338cfb65501e Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Mon, 29 Sep 2025 22:26:20 -0400 Subject: [PATCH 48/83] ADC 12-bit configuration updates --- example/thunderscope_test.cpp | 8 ++------ include/ts_common.h | 2 +- litepcie/public_h/csr.h | 20 +------------------- src/hmcad15xx.h | 2 +- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index afc17d8..b7db2f1 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -343,7 +343,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ if(is12bit) { - sampleRate = 600000000; + sampleRate = 660000000; thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, 4096); } else @@ -362,8 +362,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ printf("Bitslip Snapshot: %lu\r\n", bitslip_count); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR); printf("FRAME Debug: 0x%08x\r\n", dbg_monitor); - dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_ADC_DEBUG_ADDR); - printf("Gearbox Valid: %04x\r\n", dbg_monitor); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); printf("RANGE: 0x%08x\r\n", dbg_monitor); } @@ -380,7 +378,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ auto startTime = std::chrono::steady_clock::now(); if(sampleBuffer != NULL) { - for(uint32_t loop=0; loop < 100; loop++) + for(uint32_t loop=0; loop < 8; loop++) { uint32_t readReq = (TS_SAMPLE_BUFFER_SIZE * 0x100); //Collect Samples @@ -402,8 +400,6 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ printf("Bitslip Snapshot: %lu\r\n", bitslip_count); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR); printf("FRAME Debug: 0x%08x\r\n", dbg_monitor); - dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_ADC_DEBUG_ADDR); - printf("Gearbox Valid: %04x\r\n", dbg_monitor); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); printf("RANGE: 0x%08x\r\n", dbg_monitor); } diff --git a/include/ts_common.h b/include/ts_common.h index 94884b6..dce73cc 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -26,7 +26,7 @@ extern "C" { #define TS_IDENT_STR_LEN (256) #define TS_MAX_8BIT_SAMPLE_RATE (1000000000) -#define TS_MAX_12BIT_SAMPLE_RATE (640000000) +#define TS_MAX_12BIT_SAMPLE_RATE (660000000) #define TS_MIN_SAMPLE_RATE (15000000) #define TS_HW_ID_REV_MASK (7) diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index 4f5c473..43789bb 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-09-11 23:42:26 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-09-29 22:25:06 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -310,8 +310,6 @@ #define CSR_ADC_HMCAD1520_SAMPLE_BITS_SIZE 1 #define CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR (CSR_BASE + 0x682cL) #define CSR_ADC_HMCAD1520_FRAME_DEBUG_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_ADDR (CSR_BASE + 0x6830L) -#define CSR_ADC_HMCAD1520_ADC_DEBUG_SIZE 1 /* ADC Fields */ #define CSR_ADC_CONTROL_ACQ_EN_OFFSET 0 @@ -354,22 +352,6 @@ #define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_CLK_SIZE 8 #define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_VALID_OFFSET 8 #define CSR_ADC_HMCAD1520_FRAME_DEBUG_FRAME_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_0_VALID_OFFSET 0 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_0_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_1_VALID_OFFSET 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_1_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_2_VALID_OFFSET 2 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_2_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_3_VALID_OFFSET 3 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_3_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_4_VALID_OFFSET 4 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_4_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_5_VALID_OFFSET 5 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_5_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_6_VALID_OFFSET 6 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_6_VALID_SIZE 1 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_7_VALID_OFFSET 7 -#define CSR_ADC_HMCAD1520_ADC_DEBUG_GEARBOX_7_VALID_SIZE 1 /* FRONTEND Registers */ #define CSR_FRONTEND_BASE (CSR_BASE + 0x7000L) diff --git a/src/hmcad15xx.h b/src/hmcad15xx.h index b66f334..441beb4 100644 --- a/src/hmcad15xx.h +++ b/src/hmcad15xx.h @@ -162,7 +162,7 @@ extern "C" { #define HMCAD15_LVDS_TERM_55 (7) #define HMCAD15_CLK_DIV_DEFAULT (1) -#define HMCAD15_LVDS_PHASE_DEFAULT (HMCAD15_LVDS_PHASE_0DEG) +#define HMCAD15_LVDS_PHASE_DEFAULT (HMCAD15_LVDS_PHASE_270DEG) #define HMCAD15_SINGLE_LOW_CLK_THRESHOLD (240000000) #define HMCAD15_DUAL_LOW_CLK_THRESHOLD (120000000) From e72cd56c47803bbd13c7e576b879f22516295449 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 2 Oct 2025 02:26:57 -0400 Subject: [PATCH 49/83] Remove debug register from CSR header --- litepcie/public_h/csr.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index 43789bb..9f2619a 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-09-29 22:25:06 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-10-02 02:25:33 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -332,8 +332,6 @@ #define CSR_ADC_HMCAD1520_CONTROL_DELAY_INC_SIZE 1 #define CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET 3 #define CSR_ADC_HMCAD1520_CONTROL_STAT_RST_SIZE 1 -#define CSR_ADC_HMCAD1520_CONTROL_DATA_DELAY_INC_OFFSET 4 -#define CSR_ADC_HMCAD1520_CONTROL_DATA_DELAY_INC_SIZE 1 #define CSR_ADC_HMCAD1520_RANGE_MIN01_OFFSET 0 #define CSR_ADC_HMCAD1520_RANGE_MIN01_SIZE 8 #define CSR_ADC_HMCAD1520_RANGE_MAX01_OFFSET 8 From 43dde9e8240a58d412a6d951a97dcdd5ab615fef Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 3 Oct 2025 17:15:29 -0400 Subject: [PATCH 50/83] Catch error on invalid file in fw app --- example/thunderscope_fw.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 9fa14af..d44c464 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -27,7 +27,14 @@ static void user_write(tsHandle_t ts, const char* file_path, uint32_t offset) { - auto file_size = std::filesystem::file_size(file_path); + std::error_code err; + auto file_size = std::filesystem::file_size(file_path, err); + if(err) + { + std::cout << "Error opening \"" << file_path << "\" : " << err.message() << "\r\n"; + return; + } + int32_t status; // Open data file std::ifstream file(file_path, std::ios::binary); @@ -83,7 +90,14 @@ static void user_read(tsHandle_t ts, const char* file_path) static void fw_upgrade(tsHandle_t ts, const char* file_path) { - auto file_size = std::filesystem::file_size(file_path); + std::error_code err; + auto file_size = std::filesystem::file_size(file_path, err); + if(err) + { + std::cout << "Error opening \"" << file_path << "\" : " << err.message() << "\r\n"; + return; + } + // Open Bitstream File std::ifstream file(file_path, std::ios::binary); if(file) From 687dbb48ceebd404989910b07b01d47ca1c3da3e Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 3 Oct 2025 22:36:35 -0400 Subject: [PATCH 51/83] Revert LVDS phase change. Add some groundwork for precision mode. --- include/ts_common.h | 1 + src/adc.c | 7 ++++++- src/hmcad15xx.c | 12 +++++++++--- src/hmcad15xx.h | 6 ++++-- src/ts_channel.c | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/include/ts_common.h b/include/ts_common.h index dce73cc..63b5fd7 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -27,6 +27,7 @@ extern "C" { #define TS_MAX_8BIT_SAMPLE_RATE (1000000000) #define TS_MAX_12BIT_SAMPLE_RATE (660000000) +#define TS_MAX_14BIT_SAMPLE_RATE (125000000) #define TS_MIN_SAMPLE_RATE (15000000) #define TS_HW_ID_REV_MASK (7) diff --git a/src/adc.c b/src/adc.c index 240089f..f0fc860 100644 --- a/src/adc.c +++ b/src/adc.c @@ -225,7 +225,12 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res data_mode = HMCAD15_12_BIT; sample_bits = TS_ADC_DATA_FRAMING_12BIT; } - //else support 14-bit precise mode? + else if(resolution == 16384) + { + data_mode = HMCAD15_14_BIT; + //Precision mode uses Dual-8 LVDS + sample_bits = TS_ADC_DATA_FRAMING_8BIT; + } if(TS_STATUS_OK == hmcad15xx_set_sample_mode(&adc->adcDev, sample_rate, data_mode)) { diff --git a/src/hmcad15xx.c b/src/hmcad15xx.c index 587c71b..055627f 100644 --- a/src/hmcad15xx.c +++ b/src/hmcad15xx.c @@ -263,7 +263,8 @@ int32_t hmcad15xx_set_sample_mode(hmcad15xxADC_t* adc, uint32_t sample_rate, hmc if(((adc->mode == HMCAD15_SINGLE_CHANNEL) && (sample_rate < HMCAD15_SINGLE_LOW_CLK_THRESHOLD)) || ((adc->mode == HMCAD15_DUAL_CHANNEL) && (sample_rate < HMCAD15_DUAL_LOW_CLK_THRESHOLD)) || - ((adc->mode == HMCAD15_QUAD_CHANNEL) && (sample_rate < HMCAD15_QUAD_LOW_CLK_THRESHOLD))) + ((adc->mode == HMCAD15_QUAD_CHANNEL) && (sample_rate < HMCAD15_QUAD_LOW_CLK_THRESHOLD)) || + ((adc->mode == HMCAD15_14BIT_QUAD_CHANNEL) && (sample_rate < HMCAD15_PREC_LOW_CLK_THRESHOLD))) { adc->low_clk = 1; } @@ -335,10 +336,15 @@ static void hmcad15xxApplySampleMode(hmcad15xxADC_t* adc) HMCAD15_CLK_DIV_SET(adc->clockDiv); break; case HMCAD15_QUAD_CHANNEL: - case HMCAD15_14BIT_QUAD_CHANNEL: adc->clockDiv = HMCAD15_CLK_DIV_4; data = HMCAD15_SAMPLE_MODE_SET(adc->mode) | - HMCAD15_CLK_DIV_SET(adc->clockDiv); + HMCAD15_CLK_DIV_SET(adc->clockDiv); + break; + case HMCAD15_14BIT_QUAD_CHANNEL: + adc->clockDiv = HMCAD15_CLK_DIV_1; + data = HMCAD15_SAMPLE_MODE_SET(adc->mode) | + HMCAD15_SAMPLE_MODE_PREC | + HMCAD15_CLK_DIV_SET(adc->clockDiv); break; } hmcad15xxRegWrite(adc, HMCAD15_REG_CHAN_MODE, data); diff --git a/src/hmcad15xx.h b/src/hmcad15xx.h index 441beb4..9e8dc0e 100644 --- a/src/hmcad15xx.h +++ b/src/hmcad15xx.h @@ -63,6 +63,8 @@ extern "C" { #define HMCAD15_SAMPLE_MODE_SET(x) ((x) & 0x0F) +#define HMCAD15_SAMPLE_MODE_PREC (1 << 3) + #define HMCAD15_CLK_DIV_SET(x) (((x) & 0x03) << 8) #define HMCAD15_CLK_DIV_1 (0) #define HMCAD15_CLK_DIV_2 (1) @@ -162,7 +164,7 @@ extern "C" { #define HMCAD15_LVDS_TERM_55 (7) #define HMCAD15_CLK_DIV_DEFAULT (1) -#define HMCAD15_LVDS_PHASE_DEFAULT (HMCAD15_LVDS_PHASE_270DEG) +#define HMCAD15_LVDS_PHASE_DEFAULT (HMCAD15_LVDS_PHASE_0DEG) #define HMCAD15_SINGLE_LOW_CLK_THRESHOLD (240000000) #define HMCAD15_DUAL_LOW_CLK_THRESHOLD (120000000) @@ -181,7 +183,7 @@ typedef enum hmcad15xxDataWidth_e { HMCAD15_8_BIT = 0, HMCAD15_12_BIT = 1, - HMCAD15_14_BIT = 5 + HMCAD15_14_BIT = 4 } hmcad15xxDataWidth_t; typedef enum hmcad15xxPower_e diff --git a/src/ts_channel.c b/src/ts_channel.c index e0ff89c..4e1f6aa 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -556,7 +556,7 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin return TS_INVALID_PARAM; } - if(ts->adc.adcDev.mode == HMCAD15_SINGLE_CHANNEL) + if(ts->adc.adcDev.mode == HMCAD15_SINGLE_CHANNEL) { actual_rate = rate; } From bad51b7760722c1435d2ec04e4a6cc70eb905f77 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Mon, 6 Oct 2025 20:37:50 -0400 Subject: [PATCH 52/83] Upgrade SPIflash to handle new litespi register map. Add GW_ID field to device status. Fix bug parsing bitstream length. --- bindings/python/tslitex.pxd | 1 + example/thunderscope_fw.cpp | 1 + example/thunderscope_test.cpp | 3 + include/ts_common.h | 1 + litepcie/public_h/csr.h | 67 +++++------ src/spiflash.c | 215 +++++++++++++++++++++++----------- src/spiflash.h | 1 + src/thunderscope.c | 1 + src/ts_fw_manager.c | 2 +- 9 files changed, 189 insertions(+), 103 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index fd53389..7ebdecf 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -35,6 +35,7 @@ cdef extern from "thunderscope.h": cdef struct tsDeviceInfo_s: uint32_t device_id uint32_t hw_id + uint32_t gw_id char device_path[256] char identity[256] char serial_number[256] diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index d44c464..7025d3f 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -224,6 +224,7 @@ int main(int argc, char** argv) { printf("HW Rev Beta\n"); } + printf("Gateware Rev 0x%08X\n", infos.gw_id); if(0 == strcmp(arg, "factory_restore")) { diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index b7db2f1..71ccf76 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -524,6 +524,7 @@ static void flash_test(char* arg, file_t fd) printf("Done!\n"); outFile.flush(); outFile.close(); + free(flash_data); } else if(0 == strcmp(arg, "test")) { @@ -758,6 +759,8 @@ int main(int argc, char** argv) { printf("HW Rev Beta\n"); } + printf("Gateware Rev: 0x%08X\n", + litepcie_readl(fd, CSR_DEV_STATUS_GW_REV_ADDR)); #ifdef CSR_DNA_BASE printf("FPGA DNA: 0x%08x%08x\n", litepcie_readl(fd, CSR_DNA_ID_ADDR + 4 * 0), diff --git a/include/ts_common.h b/include/ts_common.h index 63b5fd7..c9dde37 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -57,6 +57,7 @@ typedef struct tsDeviceInfo_s { uint32_t device_id; uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ + uint32_t gw_id; // 32-bit version ID char device_path[TS_IDENT_STR_LEN]; char identity[TS_IDENT_STR_LEN]; char serial_number[TS_IDENT_STR_LEN]; diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index 9f2619a..4d754fe 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-10-02 02:25:33 +// Auto-generated by LiteX (05ddccb20) on 2025-10-04 10:32:08 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -192,41 +192,36 @@ #define CSR_CTRL_RESET_CPU_RST_OFFSET 1 #define CSR_CTRL_RESET_CPU_RST_SIZE 1 -/* SPIFLASH_CORE Registers */ -#define CSR_SPIFLASH_CORE_BASE (CSR_BASE + 0x3800L) -#define CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR (CSR_BASE + 0x3800L) -#define CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_SIZE 1 -#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_ADDR (CSR_BASE + 0x3804L) -#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_CS_ADDR (CSR_BASE + 0x3808L) -#define CSR_SPIFLASH_CORE_MASTER_CS_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR (CSR_BASE + 0x380cL) -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR (CSR_BASE + 0x3810L) -#define CSR_SPIFLASH_CORE_MASTER_RXTX_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR (CSR_BASE + 0x3814L) -#define CSR_SPIFLASH_CORE_MASTER_STATUS_SIZE 1 - -/* SPIFLASH_CORE Fields */ -#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_WRITE_ENABLE_OFFSET 0 -#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_WRITE_ENABLE_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET 0 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_SIZE 8 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET 8 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_SIZE 4 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET 16 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_SIZE 8 -#define CSR_SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET 0 -#define CSR_SPIFLASH_CORE_MASTER_STATUS_TX_READY_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET 1 -#define CSR_SPIFLASH_CORE_MASTER_STATUS_RX_READY_SIZE 1 - -/* SPIFLASH_PHY Registers */ -#define CSR_SPIFLASH_PHY_BASE (CSR_BASE + 0x4000L) -#define CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR (CSR_BASE + 0x4000L) +/* SPIFLASH Registers */ +#define CSR_SPIFLASH_BASE (CSR_BASE + 0x3800L) +#define CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR (CSR_BASE + 0x3800L) #define CSR_SPIFLASH_PHY_CLK_DIVISOR_SIZE 1 - -/* SPIFLASH_PHY Fields */ +#define CSR_SPIFLASH_MMAP_DUMMY_BITS_ADDR (CSR_BASE + 0x3804L) +#define CSR_SPIFLASH_MMAP_DUMMY_BITS_SIZE 1 +#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_ADDR (CSR_BASE + 0x3808L) +#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_SIZE 1 +#define CSR_SPIFLASH_MASTER_CS_ADDR (CSR_BASE + 0x380cL) +#define CSR_SPIFLASH_MASTER_CS_SIZE 1 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_ADDR (CSR_BASE + 0x3810L) +#define CSR_SPIFLASH_MASTER_PHYCONFIG_SIZE 1 +#define CSR_SPIFLASH_MASTER_RXTX_ADDR (CSR_BASE + 0x3814L) +#define CSR_SPIFLASH_MASTER_RXTX_SIZE 1 +#define CSR_SPIFLASH_MASTER_STATUS_ADDR (CSR_BASE + 0x3818L) +#define CSR_SPIFLASH_MASTER_STATUS_SIZE 1 + +/* SPIFLASH Fields */ +#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_WRITE_ENABLE_OFFSET 0 +#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_WRITE_ENABLE_SIZE 1 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET 0 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_SIZE 8 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET 8 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_SIZE 4 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET 16 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_SIZE 8 +#define CSR_SPIFLASH_MASTER_STATUS_TX_READY_OFFSET 0 +#define CSR_SPIFLASH_MASTER_STATUS_TX_READY_SIZE 1 +#define CSR_SPIFLASH_MASTER_STATUS_RX_READY_OFFSET 1 +#define CSR_SPIFLASH_MASTER_STATUS_RX_READY_SIZE 1 /* FLASH_ADAPTER Registers */ #define CSR_FLASH_ADAPTER_BASE (CSR_BASE + 0x4800L) @@ -273,6 +268,8 @@ #define CSR_DEV_STATUS_LEDS_SIZE 1 #define CSR_DEV_STATUS_HW_ID_ADDR (CSR_BASE + 0x6004L) #define CSR_DEV_STATUS_HW_ID_SIZE 1 +#define CSR_DEV_STATUS_GW_REV_ADDR (CSR_BASE + 0x6008L) +#define CSR_DEV_STATUS_GW_REV_SIZE 1 /* DEV_STATUS Fields */ #define CSR_DEV_STATUS_HW_ID_HW_REV_OFFSET 0 diff --git a/src/spiflash.c b/src/spiflash.c index 3637d70..cce63b3 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -27,10 +27,80 @@ #define SPI_FLASH_WRITE_ENABLE_CMD (0x06) #define SPI_FLASH_WRITE_DISABLE_CMD (0x04) +/** + * LiteSPI Register Maps + */ +/** pre-2025 liteSPI **/ +/* SPIFLASH_CORE Registers */ +#ifndef CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR +#define CSR_SPIFLASH_CORE_BASE CSR_SPIFLASH_BASE +#define CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR (CSR_SPIFLASH_CORE_BASE + 0x00L) +#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_ADDR (CSR_SPIFLASH_CORE_BASE + 0x04L) +#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_CS_ADDR (CSR_SPIFLASH_CORE_BASE + 0x08L) +#define CSR_SPIFLASH_CORE_MASTER_CS_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR (CSR_SPIFLASH_CORE_BASE + 0x0cL) +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR (CSR_SPIFLASH_CORE_BASE + 0x10L) +#define CSR_SPIFLASH_CORE_MASTER_RXTX_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR (CSR_SPIFLASH_CORE_BASE + 0x14L) +#define CSR_SPIFLASH_CORE_MASTER_STATUS_SIZE 1 +/* SPIFLASH_PHY Registers */ +#define CSR_SPIFLASH_CORE_PHY_CLK_DIVISOR_ADDR (CSR_SPIFLASH_CORE_BASE + 0x800L) +#endif + +/** post 2025 liteSPI **/ +/* SPIFLASH Registers */ +#ifndef CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR +#define CSR_SPIFLASH_BASE CSR_SPIFLASH_CORE_BASE +#define CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR (CSR_SPIFLASH_BASE + 0x00L) +#define CSR_SPIFLASH_PHY_CLK_DIVISOR_SIZE 1 +#define CSR_SPIFLASH_MMAP_DUMMY_BITS_ADDR (CSR_SPIFLASH_BASE + 0x04L) +#define CSR_SPIFLASH_MMAP_DUMMY_BITS_SIZE 1 +#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_ADDR (CSR_SPIFLASH_BASE + 0x08L) +#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_SIZE 1 +#define CSR_SPIFLASH_MASTER_CS_ADDR (CSR_SPIFLASH_BASE + 0x0cL) +#define CSR_SPIFLASH_MASTER_CS_SIZE 1 +#define CSR_SPIFLASH_MASTER_PHYCONFIG_ADDR (CSR_SPIFLASH_BASE + 0x10L) +#define CSR_SPIFLASH_MASTER_PHYCONFIG_SIZE 1 +#define CSR_SPIFLASH_MASTER_RXTX_ADDR (CSR_SPIFLASH_BASE + 0x14L) +#define CSR_SPIFLASH_MASTER_RXTX_SIZE 1 +#define CSR_SPIFLASH_MASTER_STATUS_ADDR (CSR_SPIFLASH_BASE + 0x18L) +#define CSR_SPIFLASH_MASTER_STATUS_SIZE 1 +#endif + #ifndef min #define min(x, y) (((x) < (y)) ? (x) : (y)) #endif +typedef struct spiflash_regs_s { + uint32_t PHY_CLK_DIV; + uint32_t MASTER_CS; + uint32_t MASTER_RXTX; + uint32_t MASTER_STATUS; + uint32_t MASTER_PHYCONFIG; + uint32_t DUMMY_BITS; +} spiflash_regs_t; + +static const spiflash_regs_t spiflash_regs[] = { + { + .PHY_CLK_DIV = CSR_SPIFLASH_CORE_PHY_CLK_DIVISOR_ADDR, + .MASTER_CS = CSR_SPIFLASH_CORE_MASTER_CS_ADDR, + .MASTER_RXTX = CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR, + .MASTER_STATUS = CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR, + .MASTER_PHYCONFIG = CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR, + .DUMMY_BITS = CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR + }, + { + .PHY_CLK_DIV = CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR, + .MASTER_CS = CSR_SPIFLASH_MASTER_CS_ADDR, + .MASTER_RXTX = CSR_SPIFLASH_MASTER_RXTX_ADDR, + .MASTER_STATUS = CSR_SPIFLASH_MASTER_STATUS_ADDR, + .MASTER_PHYCONFIG = CSR_SPIFLASH_MASTER_PHYCONFIG_ADDR, + .DUMMY_BITS = CSR_SPIFLASH_MMAP_DUMMY_BITS_ADDR + } +}; + static const spiflash_ops_t s25fl256s_ops = { .read = 0x6C, //4QOR .program = 0x12, //4PP @@ -45,21 +115,21 @@ static const spiflash_ops_t mx25u6432f_ops = { .cmd_addr_len = 3 }; -static uint32_t get_flash_data(file_t fd, uint32_t flash_addr, uint8_t* pData, uint32_t data_len) +static uint32_t get_flash_data(spiflash_dev_t* dev, uint32_t flash_addr, uint8_t* pData, uint32_t data_len) { const uint32_t flash_base = 0x10000; uint32_t index = 0; uint32_t flash_data = 0; - uint32_t flash_window = litepcie_readl(fd, CSR_FLASH_ADAPTER_WINDOW0_ADDR); + uint32_t flash_window = litepcie_readl(dev->fd, CSR_FLASH_ADAPTER_WINDOW0_ADDR); while(index < data_len) { if(flash_window != ((flash_addr+index) >> 16)) { flash_window = (flash_addr+index) >> 16; - litepcie_writel(fd, CSR_FLASH_ADAPTER_WINDOW0_ADDR, flash_window); + litepcie_writel(dev->fd, CSR_FLASH_ADAPTER_WINDOW0_ADDR, flash_window); } - flash_data = litepcie_readl(fd, (flash_base + ((flash_addr+index) & 0xFFFF))); + flash_data = litepcie_readl(dev->fd, (flash_base + ((flash_addr+index) & 0xFFFF))); *(uint32_t*)&pData[index] = flash_data; index += 4; } @@ -67,67 +137,65 @@ static uint32_t get_flash_data(file_t fd, uint32_t flash_addr, uint8_t* pData, u return index; } -#ifdef CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR -static void spiflash_dummy_bits_setup(file_t fd, unsigned int dummy_bits) + +static void spiflash_dummy_bits_setup(spiflash_dev_t* dev, unsigned int dummy_bits) { - litepcie_writel(fd, CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR, dummy_bits); - LOG_DEBUG("Dummy bits set to: %" PRIx32 , litepcie_readl(fd, CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR)); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].DUMMY_BITS, dummy_bits); + LOG_DEBUG("Dummy bits set to: %" PRIx32 , litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].DUMMY_BITS)); } -#endif -static void spiflash_len_mask_width_write(file_t fd, uint32_t len, uint32_t width, uint32_t mask) +static void spiflash_len_mask_width_write(spiflash_dev_t* dev, uint32_t len, uint32_t width, uint32_t mask) { - uint32_t tmp = len & ((1 << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_SIZE) - 1); - uint32_t word = tmp << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET; - tmp = width & ((1 << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_SIZE) - 1); - word |= tmp << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET; - tmp = mask & ((1 << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_SIZE) - 1); - word |= tmp << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET; - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR, word); + uint32_t tmp = len & ((1 << CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_SIZE) - 1); + uint32_t word = tmp << CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET; + tmp = width & ((1 << CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_SIZE) - 1); + word |= tmp << CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET; + tmp = mask & ((1 << CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_SIZE) - 1); + word |= tmp << CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET; + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_PHYCONFIG, word); } -static bool spiflash_tx_ready(file_t fd) +static bool spiflash_tx_ready(spiflash_dev_t* dev) { - return (litepcie_readl(fd, CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR) >> CSR_SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET) & 1; + return (litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_STATUS) >> CSR_SPIFLASH_MASTER_STATUS_TX_READY_OFFSET) & 1; } -static bool spiflash_rx_ready(file_t fd) +static bool spiflash_rx_ready(spiflash_dev_t* dev) { - return (litepcie_readl(fd, CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR) >> CSR_SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET) & 1; + return (litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_STATUS) >> CSR_SPIFLASH_MASTER_STATUS_RX_READY_OFFSET) & 1; } -static void spiflash_master_write(file_t fd, uint32_t val, size_t len, size_t width, uint32_t mask) +static void spiflash_master_write(spiflash_dev_t* dev, uint32_t val, size_t len, size_t width, uint32_t mask) { /* Be sure to empty RX queue before doing Xfer. */ - while (spiflash_rx_ready(fd)) - litepcie_readl(fd, CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR); + while (spiflash_rx_ready(dev)) + litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); /* Configure Master */ - spiflash_len_mask_width_write(fd, 8*len, width, mask); + spiflash_len_mask_width_write(dev, 8*len, width, mask); /* Set CS. */ - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_CS_ADDR, 1UL); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_CS, 1UL); /* Do Xfer. */ - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR, val); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX, val); - while (!spiflash_rx_ready(fd)); + while (!spiflash_rx_ready(dev)); /* Clear CS. */ - // spiflash_core_master_cs_write(0); - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_CS_ADDR, 0UL); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_CS, 0UL); } static uint8_t w_buf[SPI_FLASH_PROG_SIZE + 5]; static uint8_t r_buf[SPI_FLASH_PROG_SIZE + 5]; -static void transfer_cmd(file_t fd, const uint8_t *bs, uint8_t *resp, int len) +static void transfer_cmd(spiflash_dev_t* dev, const uint8_t *bs, uint8_t *resp, int len) { uint32_t xfer_word = 0; uint32_t xfer_num_bytes = 0; - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_CS_ADDR, 1UL); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_CS, 1UL); for (int i=0; i < len; i+=4) { @@ -141,19 +209,22 @@ static void transfer_cmd(file_t fd, const uint8_t *bs, uint8_t *resp, int len) xfer_word = (xfer_word << 8) | (uint32_t)bs[i+3]; - spiflash_len_mask_width_write(fd, (8*xfer_num_bytes), 1, 1); + spiflash_len_mask_width_write(dev, (8*xfer_num_bytes), 1, 1); /* wait for tx ready */ - while (!spiflash_tx_ready(fd)); + while (!spiflash_tx_ready(dev)) + { + litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); + } - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR, (uint32_t)xfer_word); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX, (uint32_t)xfer_word); /* wait for rx ready */ - while (!spiflash_rx_ready(fd)) + while (!spiflash_rx_ready(dev)) { NS_DELAY(250); } - xfer_word = litepcie_readl(fd, CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR); + xfer_word = litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); resp[i] = (uint8_t)((xfer_word >> (8*(xfer_num_bytes-1))) & 0xFF); if(xfer_num_bytes > 1) resp[i+1] = (uint8_t)((xfer_word >> (8*(xfer_num_bytes-2))) & 0xFF); @@ -163,17 +234,17 @@ static void transfer_cmd(file_t fd, const uint8_t *bs, uint8_t *resp, int len) resp[i+3] = (uint8_t)((xfer_word) & 0xFF); } - litepcie_writel(fd, CSR_SPIFLASH_CORE_MASTER_CS_ADDR, 0UL); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_CS, 0UL); } -static uint32_t spiflash_read_id_register(file_t fd) +static uint32_t spiflash_read_id_register(spiflash_dev_t* dev) { uint8_t buf[4]; w_buf[0] = SPI_FLASH_JEDEC_READ_ID_CMD; w_buf[1] = 0x00; w_buf[2] = 0x00; w_buf[3] = 0x00; - transfer_cmd(fd, w_buf, buf, 4); + transfer_cmd(dev, w_buf, buf, 4); LOG_DEBUG("[ID: %02x %02x %02x %02x]", buf[0], buf[1], buf[2], buf[3]); @@ -181,30 +252,30 @@ static uint32_t spiflash_read_id_register(file_t fd) return flash_id; } -static uint32_t spiflash_read_status_register(file_t fd) +static uint32_t spiflash_read_status_register(spiflash_dev_t* dev) { uint8_t buf[2]; w_buf[0] = SPI_FLASH_JEDEC_READ_STATUS_REG_1_CMD; w_buf[1] = 0x00; - transfer_cmd(fd, w_buf, buf, 2); + transfer_cmd(dev, w_buf, buf, 2); LOG_DEBUG("[SR: %02x %02x]", buf[0], buf[1]); return buf[1]; } -static void spiflash_write_enable(file_t fd) +static void spiflash_write_enable(spiflash_dev_t* dev) { uint8_t buf[1]; w_buf[0] = SPI_FLASH_WRITE_ENABLE_CMD; - transfer_cmd(fd, w_buf, buf, 1); + transfer_cmd(dev, w_buf, buf, 1); } -static void spiflash_write_disable(file_t fd) +static void spiflash_write_disable(spiflash_dev_t* dev) { uint8_t buf[1]; w_buf[0] = SPI_FLASH_WRITE_DISABLE_CMD; - transfer_cmd(fd, w_buf, buf, 1); + transfer_cmd(dev, w_buf, buf, 1); } static void page_program(spiflash_dev_t* dev, uint32_t addr, uint8_t *data, int len) @@ -219,7 +290,7 @@ static void page_program(spiflash_dev_t* dev, uint32_t addr, uint8_t *data, int w_buf[cmd_idx++] = addr>>8; w_buf[cmd_idx++] = addr>>0; memcpy((void *)(&w_buf[cmd_idx]), (void *)data, len); - transfer_cmd(dev->fd, w_buf, r_buf, len+cmd_idx); + transfer_cmd(dev, w_buf, r_buf, len+cmd_idx); } static void spiflash_sector_erase(spiflash_dev_t* dev, uint32_t addr) @@ -233,7 +304,7 @@ static void spiflash_sector_erase(spiflash_dev_t* dev, uint32_t addr) w_buf[cmd_idx++] = addr>>16; w_buf[cmd_idx++] = addr>>8; w_buf[cmd_idx++] = addr>>0; - transfer_cmd(dev->fd, w_buf, r_buf, cmd_idx); + transfer_cmd(dev, w_buf, r_buf, cmd_idx); } int32_t spiflash_erase(spiflash_dev_t* dev, uint32_t addr, uint32_t len) @@ -249,17 +320,17 @@ int32_t spiflash_erase(spiflash_dev_t* dev, uint32_t addr, uint32_t len) for (i=0; ifd); + spiflash_write_enable(dev); spiflash_sector_erase(dev, addr+i); - while (spiflash_read_status_register(dev->fd) & 1) { - NS_DELAY(250000000); + while (spiflash_read_status_register(dev) & 1) { + NS_DELAY(10000000); } /* check if region was really erased */ for (j = 0; j < SPI_FLASH_ERASE_SIZE; j+=4) { uint32_t flash_word; - get_flash_data(dev->fd, (addr+i+j), (uint8_t*)&flash_word, sizeof(uint32_t)); + get_flash_data(dev, (addr+i+j), (uint8_t*)&flash_word, sizeof(uint32_t)); if (flash_word != 0xffffffff) { LOG_ERROR("Error: location 0x%08x not erased (0x%08x)", addr+i+j, flash_word); return TS_STATUS_ERROR; @@ -272,7 +343,7 @@ int32_t spiflash_erase(spiflash_dev_t* dev, uint32_t addr, uint32_t len) dev->op_progress(dev->op_progress_ctx, SPI_FLASH_ERASE_SIZE, len); } } - spiflash_write_disable(dev->fd); + spiflash_write_disable(dev); return TS_STATUS_OK; } @@ -287,18 +358,18 @@ int32_t spiflash_write(spiflash_dev_t* dev, uint32_t addr, const uint8_t *pData, LOG_DEBUG("Write SPI Flash @0x%08lx", ((uint32_t)addr)); while(w_len) { - spiflash_write_enable(dev->fd); + spiflash_write_enable(dev); page_program(dev, addr+offset, (uint8_t*)pData+offset, w_len); - while(spiflash_read_status_register(dev->fd) & 1) { + while(spiflash_read_status_register(dev) & 1) { NS_DELAY(10000); } - get_flash_data(dev->fd, addr+offset, (uint8_t*)r_buf, w_len); + get_flash_data(dev, addr+offset, (uint8_t*)r_buf, w_len); for (j = 0; j < w_len; j++) { if (r_buf[j] != pData[offset+j]) { LOG_ERROR("Error: verify failed at 0x%08x (0x%02x should be 0x%02x)", (uint32_t)(addr+offset+j), r_buf[j], pData[offset+j]); - spiflash_write_disable(dev->fd); + spiflash_write_disable(dev); return TS_STATUS_ERROR; } } @@ -313,7 +384,7 @@ int32_t spiflash_write(spiflash_dev_t* dev, uint32_t addr, const uint8_t *pData, w_len = min(len-offset, SPI_FLASH_PROG_SIZE); res = offset; } - spiflash_write_disable(dev->fd); + spiflash_write_disable(dev); return res; } @@ -322,18 +393,28 @@ int32_t spiflash_init(file_t fd, spiflash_dev_t* dev) { uint32_t flash_id = 0; uint32_t divisor = SPI_FLASH_CLK_DIV_DEFAULT; + uint32_t ip_version = 0; dev->fd = fd; -#ifdef CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR - spiflash_dummy_bits_setup(fd, 8); -#endif + // Get IP Version + ip_version = litepcie_readl(fd, CSR_DEV_STATUS_GW_REV_ADDR); + if(ip_version >= 0x0300) + { + dev->ip_rev = 1; + } + else + { + dev->ip_rev = 0; + } + + spiflash_dummy_bits_setup(dev, 8); while(divisor <= SPI_FLASH_CLK_DIV_MAX) { - litepcie_writel(fd, CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR, divisor); - flash_id = spiflash_read_id_register(dev->fd); //First ID read returns garbage? - flash_id = spiflash_read_id_register(dev->fd); + litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].PHY_CLK_DIV, divisor); + flash_id = spiflash_read_id_register(dev); //First ID read returns garbage? + flash_id = spiflash_read_id_register(dev); if((flash_id == 0x010219) || (flash_id == 0xC22017) || @@ -405,7 +486,7 @@ int32_t spiflash_read(spiflash_dev_t* dev, uint32_t addr, uint8_t* pData, uint32 if(addr % 4 != 0) { - read_len = get_flash_data(dev->fd, tempAddr, flashBytes, 4); + read_len = get_flash_data(dev, tempAddr, flashBytes, 4); if( 4 != read_len ) { LOG_ERROR("Failed to read with length 0"); @@ -418,7 +499,7 @@ int32_t spiflash_read(spiflash_dev_t* dev, uint32_t addr, uint8_t* pData, uint32 remaining -= read_len; } - read_len = get_flash_data(dev->fd, addr, pData, (remaining/4)*4); + read_len = get_flash_data(dev, addr, pData, (remaining/4)*4); addr += read_len; pData += read_len; remaining -= read_len; @@ -430,7 +511,7 @@ int32_t spiflash_read(spiflash_dev_t* dev, uint32_t addr, uint8_t* pData, uint32 else if(0 != remaining) { //Should only be 1-3 bytes left - if( 4 != get_flash_data(dev->fd, addr, flashBytes, 4)) + if( 4 != get_flash_data(dev, addr, flashBytes, 4)) { LOG_ERROR("Failed to read end of data (%d / %d)", read_len, len); return TS_STATUS_ERROR; diff --git a/src/spiflash.h b/src/spiflash.h index 1636685..f279148 100644 --- a/src/spiflash.h +++ b/src/spiflash.h @@ -33,6 +33,7 @@ typedef struct spiflash_ops_s { typedef struct spiflash_dev_s { file_t fd; + uint32_t ip_rev; uint8_t mfg_code; uint16_t part_id; spiflash_ops_t ops; diff --git a/src/thunderscope.c b/src/thunderscope.c index ad4ae11..ab5c4b8 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -57,6 +57,7 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) { info->device_id = devIndex; info->hw_id = litepcie_readl(testDev, CSR_DEV_STATUS_HW_ID_ADDR); + info->gw_id = litepcie_readl(testDev, CSR_DEV_STATUS_GW_REV_ADDR); //Copy device identifier for (uint32_t i = 0; i < TS_IDENT_STR_LEN; i++) { diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index 472af88..4a46d24 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -58,7 +58,7 @@ static uint32_t ts_fw_get_idcode(file_t fd) static const char* ts_fw_parse_bit_header(const char* header, const char** part, uint32_t* bin_len) { - const char* position = header; + const uint8_t* position = header; uint16_t key_len = 0; //First Field ('0FF0...') From 6994bb4ec2f3a4c5bbde679a661d30903a08548c Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 16 Oct 2025 20:00:00 -0400 Subject: [PATCH 53/83] Set Coupling polarity by HW rev --- src/afe.c | 18 +++++++++++++++--- src/afe.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/afe.c b/src/afe.c index 89934c1..bb4bc39 100644 --- a/src/afe.c +++ b/src/afe.c @@ -44,13 +44,18 @@ int32_t ts_afe_init(ts_afe_t* afe, uint8_t channel, spi_dev_t afe_amp, i2c_t tri afe->trimDacCh = dacCh; afe->trimPot = trimPot; afe->trimPotCh = potCh; - if(isBetaDevice(trimDac.fd)) + if(isBetaDevice(trimPot.fd)) { afe->trimPotBits = MCP4432_NUM_BITS; } else { afe->trimPotBits = MCP4452_NUM_BITS; + + if(litepcie_readl(coupling.fd, CSR_DEV_STATUS_HW_ID_ADDR) & TS_HW_ID_REV_MASK > 0) + { + afe->couplingInverted = true; + } } afe->termPin = termination; afe->attenuatorPin = attenuator; @@ -383,13 +388,20 @@ int32_t ts_afe_coupling_control(ts_afe_t* afe, tsChannelCoupling_t coupled) case TS_COUPLE_DC: { LOG_DEBUG("Clear Coupling %x", afe->couplingPin.bit_mask); - gpio_clear(afe->couplingPin); + if(afe->couplingInverted) + gpio_set(afe->couplingPin); + else + gpio_clear(afe->couplingPin); + break; } case TS_COUPLE_AC: { LOG_DEBUG("Set Coupling %x", afe->couplingPin.bit_mask); - gpio_set(afe->couplingPin); + if(afe->couplingInverted) + gpio_clear(afe->couplingPin); + else + gpio_set(afe->couplingPin); break; } default: diff --git a/src/afe.h b/src/afe.h index 5f32ab4..6ad1d9c 100644 --- a/src/afe.h +++ b/src/afe.h @@ -37,6 +37,7 @@ typedef struct ts_afe_s gpio_t attenuatorPin; bool isAttenuated; gpio_t couplingPin; + bool couplingInverted; tsChannelCoupling_t coupling; tsChannelCalibration_t cal; }ts_afe_t; From 682d8d8f5769214bc9f514c984507a6f3cffb8dc Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 19 Oct 2025 00:09:39 -0400 Subject: [PATCH 54/83] Fixes for spiflash registers and divisor. Add gateware and LiteX version registers. Add ADC Frame sync status bit. Bug fix for listing available devices on Windows. --- bindings/python/tslitex.pxd | 2 + example/thunderscope_fw.cpp | 25 ++++++- example/thunderscope_test.cpp | 3 + include/ts_common.h | 9 ++- litepcie/public_h/csr.h | 69 +++++++++++--------- litepcie/src/litepcie_helpers.c | 2 +- src/spiflash.c | 111 ++++++++++++++------------------ src/thunderscope.c | 1 + src/ts_channel.c | 5 +- 9 files changed, 131 insertions(+), 96 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 7ebdecf..3f23899 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -36,6 +36,7 @@ cdef extern from "thunderscope.h": uint32_t device_id uint32_t hw_id uint32_t gw_id + uint32_t litex char device_path[256] char identity[256] char serial_number[256] @@ -70,6 +71,7 @@ cdef extern from "thunderscope.h": uint32_t adc_lost_buffer_count uint32_t flags uint8_t adc_state + uint8_t adc_sync uint8_t power_state uint8_t pll_state uint8_t afe_state diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 7025d3f..6a6a5ad 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -199,6 +199,28 @@ int main(int argc, char** argv) exit(0); } + if(0 == strcmp(arg, "list")) + { + uint32_t i = 0; + while(TS_STATUS_OK == thunderscopeListDevices(i, &infos)) + { + if(i==0) + { + printf("Found ThunderScope(s):\n"); + } + printf("\t%3d | Serial Number: %s\n", i, infos.serial_number); + printf("\t | HW Rev: 0x%x\n", infos.hw_id); + printf("\t | GW Rev: 0x%x\n", infos.gw_id); + printf("\t | LiteX Rev: 0x%x\n", infos.litex); + i++; + } + if(i == 0) + { + printf("No devices present\n"); + } + exit(EXIT_SUCCESS); + } + result = thunderscopeListDevices(idx, &infos); if(result == TS_STATUS_ERROR) { @@ -224,7 +246,8 @@ int main(int argc, char** argv) { printf("HW Rev Beta\n"); } - printf("Gateware Rev 0x%08X\n", infos.gw_id); + printf("Gateware Rev 0x%08X\n", infos.gw_id); + printf("LiteX Release 0x%08X\n", infos.litex); if(0 == strcmp(arg, "factory_restore")) { diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 71ccf76..e4f270e 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -761,6 +761,9 @@ int main(int argc, char** argv) } printf("Gateware Rev: 0x%08X\n", litepcie_readl(fd, CSR_DEV_STATUS_GW_REV_ADDR)); + printf("LiteX Release: 0x%08X\n", + litepcie_readl(fd, CSR_DEV_STATUS_LITEX_REL_ADDR)); + #ifdef CSR_DNA_BASE printf("FPGA DNA: 0x%08x%08x\n", litepcie_readl(fd, CSR_DNA_ID_ADDR + 4 * 0), diff --git a/include/ts_common.h b/include/ts_common.h index c9dde37..3d76558 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -34,6 +34,11 @@ extern "C" { #define TS_HW_ID_VARIANT_MASK (1 << 8) #define TS_HW_ID_VALID_MASK (1 << 9) +#define TS_GW_VERSION(major, minor, patch) ((((major) & 0xFFFF) << 16) + \ + (((minor) & 0xFF) << 8) + \ + (((patch) & 0x3F) << 1)) + +#define LITEX_VERSION(major, minor) ((((major) & 0xFFFF) << 16) + ((minor) & 0xFFFF)) /** * @brief Opaque Handle to a Thunderscope device instance * @@ -57,7 +62,8 @@ typedef struct tsDeviceInfo_s { uint32_t device_id; uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ - uint32_t gw_id; // 32-bit version ID + uint32_t gw_id; /**< 32-bit version ID: gw_id[31:16] - Major, gw_id[15:8] - Minor, gw_id[7:1] - Patch, gw_id[0] - next */ + uint32_t litex; /**< 32-bit LiteX version: litex[31:16] - Year, litex[15:0] - Month */ char device_path[TS_IDENT_STR_LEN]; char identity[TS_IDENT_STR_LEN]; char serial_number[TS_IDENT_STR_LEN]; @@ -94,6 +100,7 @@ typedef struct tsScopeState_s uint32_t flags; struct { uint8_t adc_state:1; + uint8_t adc_sync:1; uint8_t power_state:1; uint8_t pll_state:1; uint8_t afe_state:1; diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index 4d754fe..19a16b4 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (05ddccb20) on 2025-10-04 10:32:08 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-10-18 14:34:05 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -192,36 +192,41 @@ #define CSR_CTRL_RESET_CPU_RST_OFFSET 1 #define CSR_CTRL_RESET_CPU_RST_SIZE 1 -/* SPIFLASH Registers */ -#define CSR_SPIFLASH_BASE (CSR_BASE + 0x3800L) -#define CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR (CSR_BASE + 0x3800L) +/* SPIFLASH_CORE Registers */ +#define CSR_SPIFLASH_CORE_BASE (CSR_BASE + 0x3800L) +#define CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR (CSR_BASE + 0x3800L) +#define CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_SIZE 1 +#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_ADDR (CSR_BASE + 0x3804L) +#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_CS_ADDR (CSR_BASE + 0x3808L) +#define CSR_SPIFLASH_CORE_MASTER_CS_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR (CSR_BASE + 0x380cL) +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR (CSR_BASE + 0x3810L) +#define CSR_SPIFLASH_CORE_MASTER_RXTX_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR (CSR_BASE + 0x3814L) +#define CSR_SPIFLASH_CORE_MASTER_STATUS_SIZE 1 + +/* SPIFLASH_CORE Fields */ +#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_WRITE_ENABLE_OFFSET 0 +#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_WRITE_ENABLE_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET 0 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_SIZE 8 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET 8 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_SIZE 4 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET 16 +#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_SIZE 8 +#define CSR_SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET 0 +#define CSR_SPIFLASH_CORE_MASTER_STATUS_TX_READY_SIZE 1 +#define CSR_SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET 1 +#define CSR_SPIFLASH_CORE_MASTER_STATUS_RX_READY_SIZE 1 + +/* SPIFLASH_PHY Registers */ +#define CSR_SPIFLASH_PHY_BASE (CSR_BASE + 0x4000L) +#define CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR (CSR_BASE + 0x4000L) #define CSR_SPIFLASH_PHY_CLK_DIVISOR_SIZE 1 -#define CSR_SPIFLASH_MMAP_DUMMY_BITS_ADDR (CSR_BASE + 0x3804L) -#define CSR_SPIFLASH_MMAP_DUMMY_BITS_SIZE 1 -#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_ADDR (CSR_BASE + 0x3808L) -#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_SIZE 1 -#define CSR_SPIFLASH_MASTER_CS_ADDR (CSR_BASE + 0x380cL) -#define CSR_SPIFLASH_MASTER_CS_SIZE 1 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_ADDR (CSR_BASE + 0x3810L) -#define CSR_SPIFLASH_MASTER_PHYCONFIG_SIZE 1 -#define CSR_SPIFLASH_MASTER_RXTX_ADDR (CSR_BASE + 0x3814L) -#define CSR_SPIFLASH_MASTER_RXTX_SIZE 1 -#define CSR_SPIFLASH_MASTER_STATUS_ADDR (CSR_BASE + 0x3818L) -#define CSR_SPIFLASH_MASTER_STATUS_SIZE 1 - -/* SPIFLASH Fields */ -#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_WRITE_ENABLE_OFFSET 0 -#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_WRITE_ENABLE_SIZE 1 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET 0 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_SIZE 8 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET 8 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_SIZE 4 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET 16 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_SIZE 8 -#define CSR_SPIFLASH_MASTER_STATUS_TX_READY_OFFSET 0 -#define CSR_SPIFLASH_MASTER_STATUS_TX_READY_SIZE 1 -#define CSR_SPIFLASH_MASTER_STATUS_RX_READY_OFFSET 1 -#define CSR_SPIFLASH_MASTER_STATUS_RX_READY_SIZE 1 + +/* SPIFLASH_PHY Fields */ /* FLASH_ADAPTER Registers */ #define CSR_FLASH_ADAPTER_BASE (CSR_BASE + 0x4800L) @@ -270,6 +275,8 @@ #define CSR_DEV_STATUS_HW_ID_SIZE 1 #define CSR_DEV_STATUS_GW_REV_ADDR (CSR_BASE + 0x6008L) #define CSR_DEV_STATUS_GW_REV_SIZE 1 +#define CSR_DEV_STATUS_LITEX_REL_ADDR (CSR_BASE + 0x600cL) +#define CSR_DEV_STATUS_LITEX_REL_SIZE 1 /* DEV_STATUS Fields */ #define CSR_DEV_STATUS_HW_ID_HW_REV_OFFSET 0 @@ -319,6 +326,8 @@ #define CSR_ADC_CONTROL_PWR_DOWN_SIZE 1 #define CSR_ADC_STATUS_ACQ_PG_OFFSET 0 #define CSR_ADC_STATUS_ACQ_PG_SIZE 1 +#define CSR_ADC_STATUS_FRAME_SYNC_OFFSET 1 +#define CSR_ADC_STATUS_FRAME_SYNC_SIZE 1 #define CSR_ADC_TRIGGER_CONTROL_ENABLE_OFFSET 0 #define CSR_ADC_TRIGGER_CONTROL_ENABLE_SIZE 1 #define CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET 0 diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index b00eb5b..15b0c83 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -42,7 +42,7 @@ static void getDeviceName(PWCHAR devName, DWORD maxLen, DWORD devIdx) HDEVINFO hwDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_litepciedrv, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); devData.cbSize = sizeof(devData); - if (!SetupDiEnumDeviceInterfaces(hwDevInfo, NULL, &GUID_DEVINTERFACE_litepciedrv, 0, &devData)) + if (!SetupDiEnumDeviceInterfaces(hwDevInfo, NULL, &GUID_DEVINTERFACE_litepciedrv, devIdx, &devData)) { //Print Error fprintf(stderr, "No Devices Found\n"); diff --git a/src/spiflash.c b/src/spiflash.c index cce63b3..a859468 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -19,7 +19,7 @@ #define SPI_FLASH_WINDOW_SIZE 0x10000 -#define SPI_FLASH_CLK_DIV_DEFAULT (1) +#define SPI_FLASH_CLK_DIV_DEFAULT (3) #define SPI_FLASH_CLK_DIV_MAX (10) #define SPI_FLASH_JEDEC_READ_ID_CMD (0x9F) @@ -31,43 +31,23 @@ * LiteSPI Register Maps */ /** pre-2025 liteSPI **/ -/* SPIFLASH_CORE Registers */ -#ifndef CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR +#ifndef CSR_SPIFLASH_CORE_BASE #define CSR_SPIFLASH_CORE_BASE CSR_SPIFLASH_BASE -#define CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR (CSR_SPIFLASH_CORE_BASE + 0x00L) -#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_ADDR (CSR_SPIFLASH_CORE_BASE + 0x04L) -#define CSR_SPIFLASH_CORE_MMAP_WRITE_CONFIG_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_CS_ADDR (CSR_SPIFLASH_CORE_BASE + 0x08L) -#define CSR_SPIFLASH_CORE_MASTER_CS_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR (CSR_SPIFLASH_CORE_BASE + 0x0cL) -#define CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR (CSR_SPIFLASH_CORE_BASE + 0x10L) -#define CSR_SPIFLASH_CORE_MASTER_RXTX_SIZE 1 -#define CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR (CSR_SPIFLASH_CORE_BASE + 0x14L) -#define CSR_SPIFLASH_CORE_MASTER_STATUS_SIZE 1 -/* SPIFLASH_PHY Registers */ -#define CSR_SPIFLASH_CORE_PHY_CLK_DIVISOR_ADDR (CSR_SPIFLASH_CORE_BASE + 0x800L) #endif /** post 2025 liteSPI **/ -/* SPIFLASH Registers */ -#ifndef CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR +#ifndef CSR_SPIFLASH_BASE #define CSR_SPIFLASH_BASE CSR_SPIFLASH_CORE_BASE -#define CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR (CSR_SPIFLASH_BASE + 0x00L) -#define CSR_SPIFLASH_PHY_CLK_DIVISOR_SIZE 1 -#define CSR_SPIFLASH_MMAP_DUMMY_BITS_ADDR (CSR_SPIFLASH_BASE + 0x04L) -#define CSR_SPIFLASH_MMAP_DUMMY_BITS_SIZE 1 -#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_ADDR (CSR_SPIFLASH_BASE + 0x08L) -#define CSR_SPIFLASH_MMAP_WRITE_CONFIG_SIZE 1 -#define CSR_SPIFLASH_MASTER_CS_ADDR (CSR_SPIFLASH_BASE + 0x0cL) -#define CSR_SPIFLASH_MASTER_CS_SIZE 1 -#define CSR_SPIFLASH_MASTER_PHYCONFIG_ADDR (CSR_SPIFLASH_BASE + 0x10L) -#define CSR_SPIFLASH_MASTER_PHYCONFIG_SIZE 1 -#define CSR_SPIFLASH_MASTER_RXTX_ADDR (CSR_SPIFLASH_BASE + 0x14L) -#define CSR_SPIFLASH_MASTER_RXTX_SIZE 1 -#define CSR_SPIFLASH_MASTER_STATUS_ADDR (CSR_SPIFLASH_BASE + 0x18L) -#define CSR_SPIFLASH_MASTER_STATUS_SIZE 1 #endif +#define SPIFLASH_PHYCONFIG_LEN_OFFSET 0 +#define SPIFLASH_PHYCONFIG_LEN_SIZE 8 +#define SPIFLASH_PHYCONFIG_WIDTH_OFFSET 8 +#define SPIFLASH_PHYCONFIG_WIDTH_SIZE 4 +#define SPIFLASH_PHYCONFIG_MASK_OFFSET 16 +#define SPIFLASH_PHYCONFIG_MASK_SIZE 8 + +#define SPIFLASH_STATUS_TX_READY_OFFSET 0 +#define SPIFLASH_STATUS_RX_READY_OFFSET 1 #ifndef min #define min(x, y) (((x) < (y)) ? (x) : (y)) @@ -84,20 +64,20 @@ typedef struct spiflash_regs_s { static const spiflash_regs_t spiflash_regs[] = { { - .PHY_CLK_DIV = CSR_SPIFLASH_CORE_PHY_CLK_DIVISOR_ADDR, - .MASTER_CS = CSR_SPIFLASH_CORE_MASTER_CS_ADDR, - .MASTER_RXTX = CSR_SPIFLASH_CORE_MASTER_RXTX_ADDR, - .MASTER_STATUS = CSR_SPIFLASH_CORE_MASTER_STATUS_ADDR, - .MASTER_PHYCONFIG = CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_ADDR, - .DUMMY_BITS = CSR_SPIFLASH_CORE_MMAP_DUMMY_BITS_ADDR + .PHY_CLK_DIV = (CSR_SPIFLASH_CORE_BASE + 0x800L), + .MASTER_CS = (CSR_SPIFLASH_CORE_BASE + 0x08L), + .MASTER_RXTX = (CSR_SPIFLASH_CORE_BASE + 0x10L), + .MASTER_STATUS = (CSR_SPIFLASH_CORE_BASE + 0x14L), + .MASTER_PHYCONFIG = (CSR_SPIFLASH_CORE_BASE + 0x0cL), + .DUMMY_BITS = (CSR_SPIFLASH_CORE_BASE + 0x00L) }, { - .PHY_CLK_DIV = CSR_SPIFLASH_PHY_CLK_DIVISOR_ADDR, - .MASTER_CS = CSR_SPIFLASH_MASTER_CS_ADDR, - .MASTER_RXTX = CSR_SPIFLASH_MASTER_RXTX_ADDR, - .MASTER_STATUS = CSR_SPIFLASH_MASTER_STATUS_ADDR, - .MASTER_PHYCONFIG = CSR_SPIFLASH_MASTER_PHYCONFIG_ADDR, - .DUMMY_BITS = CSR_SPIFLASH_MMAP_DUMMY_BITS_ADDR + .PHY_CLK_DIV = (CSR_SPIFLASH_BASE + 0x00L), + .MASTER_CS = (CSR_SPIFLASH_BASE + 0x0cL), + .MASTER_RXTX = (CSR_SPIFLASH_BASE + 0x14L), + .MASTER_STATUS = (CSR_SPIFLASH_BASE + 0x18L), + .MASTER_PHYCONFIG = (CSR_SPIFLASH_BASE + 0x10L), + .DUMMY_BITS = (CSR_SPIFLASH_BASE + 0x04L) } }; @@ -147,23 +127,23 @@ static void spiflash_dummy_bits_setup(spiflash_dev_t* dev, unsigned int dummy_bi static void spiflash_len_mask_width_write(spiflash_dev_t* dev, uint32_t len, uint32_t width, uint32_t mask) { - uint32_t tmp = len & ((1 << CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_SIZE) - 1); - uint32_t word = tmp << CSR_SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET; - tmp = width & ((1 << CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_SIZE) - 1); - word |= tmp << CSR_SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET; - tmp = mask & ((1 << CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_SIZE) - 1); - word |= tmp << CSR_SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET; + uint32_t tmp = len & ((1 << SPIFLASH_PHYCONFIG_LEN_SIZE) - 1); + uint32_t word = tmp << SPIFLASH_PHYCONFIG_LEN_OFFSET; + tmp = width & ((1 << SPIFLASH_PHYCONFIG_WIDTH_SIZE) - 1); + word |= tmp << SPIFLASH_PHYCONFIG_WIDTH_OFFSET; + tmp = mask & ((1 << SPIFLASH_PHYCONFIG_MASK_SIZE) - 1); + word |= tmp << SPIFLASH_PHYCONFIG_MASK_OFFSET; litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_PHYCONFIG, word); } static bool spiflash_tx_ready(spiflash_dev_t* dev) { - return (litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_STATUS) >> CSR_SPIFLASH_MASTER_STATUS_TX_READY_OFFSET) & 1; + return (litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_STATUS) >> SPIFLASH_STATUS_TX_READY_OFFSET) & 1; } static bool spiflash_rx_ready(spiflash_dev_t* dev) { - return (litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_STATUS) >> CSR_SPIFLASH_MASTER_STATUS_RX_READY_OFFSET) & 1; + return (litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_STATUS) >> SPIFLASH_STATUS_RX_READY_OFFSET) & 1; } static void spiflash_master_write(spiflash_dev_t* dev, uint32_t val, size_t len, size_t width, uint32_t mask) @@ -197,6 +177,19 @@ static void transfer_cmd(spiflash_dev_t* dev, const uint8_t *bs, uint8_t *resp, litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_CS, 1UL); + /* Flush RX */ + while (spiflash_rx_ready(dev)) + { + litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); + } + + /* wait for tx ready */ + while (!spiflash_tx_ready(dev)) + { + litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); + } + + for (int i=0; i < len; i+=4) { xfer_num_bytes = min((len - i), 4); @@ -207,16 +200,9 @@ static void transfer_cmd(spiflash_dev_t* dev, const uint8_t *bs, uint8_t *resp, xfer_word = (xfer_word << 8) | (uint32_t)bs[i+2]; if(xfer_num_bytes > 3) xfer_word = (xfer_word << 8) | (uint32_t)bs[i+3]; - - + spiflash_len_mask_width_write(dev, (8*xfer_num_bytes), 1, 1); - /* wait for tx ready */ - while (!spiflash_tx_ready(dev)) - { - litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); - } - litepcie_writel(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX, (uint32_t)xfer_word); /* wait for rx ready */ @@ -224,6 +210,7 @@ static void transfer_cmd(spiflash_dev_t* dev, const uint8_t *bs, uint8_t *resp, { NS_DELAY(250); } + xfer_word = litepcie_readl(dev->fd, spiflash_regs[dev->ip_rev].MASTER_RXTX); resp[i] = (uint8_t)((xfer_word >> (8*(xfer_num_bytes-1))) & 0xFF); if(xfer_num_bytes > 1) @@ -398,8 +385,8 @@ int32_t spiflash_init(file_t fd, spiflash_dev_t* dev) dev->fd = fd; // Get IP Version - ip_version = litepcie_readl(fd, CSR_DEV_STATUS_GW_REV_ADDR); - if(ip_version >= 0x0300) + ip_version = litepcie_readl(fd, CSR_DEV_STATUS_LITEX_REL_ADDR); + if(ip_version >= LITEX_VERSION(2025, 4)) // 2025.4+ { dev->ip_rev = 1; } diff --git a/src/thunderscope.c b/src/thunderscope.c index ab5c4b8..6904762 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -58,6 +58,7 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) info->device_id = devIndex; info->hw_id = litepcie_readl(testDev, CSR_DEV_STATUS_HW_ID_ADDR); info->gw_id = litepcie_readl(testDev, CSR_DEV_STATUS_GW_REV_ADDR); + info->litex = litepcie_readl(testDev, CSR_DEV_STATUS_LITEX_REL_ADDR); //Copy device identifier for (uint32_t i = 0; i < TS_IDENT_STR_LEN; i++) { diff --git a/src/ts_channel.c b/src/ts_channel.c index 4e1f6aa..426e050 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -521,9 +521,12 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels) tsScopeState_t state = {0}; return state; } + ts_channel_t* pTsHdl = (ts_channel_t*)tsChannels; + + pTsHdl->status.adc_sync = (litepcie_readl(pTsHdl->ctrl_handle, CSR_ADC_STATUS_ADDR) & (1 << CSR_ADC_STATUS_FRAME_SYNC_OFFSET)) ? 1 : 0; //Update XADC values - ts_channel_health_update((ts_channel_t*)tsChannels); + ts_channel_health_update(pTsHdl); return ((ts_channel_t*)tsChannels)->status; } From ecf3871a923120045e813bb63fce7b888552fe9f Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 19 Oct 2025 20:46:41 -0400 Subject: [PATCH 55/83] Populate info fields when opening TS. --- litepcie/src/litepcie_helpers.c | 1 - src/thunderscope.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 15b0c83..8f8dcc0 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -72,7 +72,6 @@ static void getDeviceName(PWCHAR devName, DWORD maxLen, DWORD devIdx) if (SetupDiGetDeviceInterfaceDetail(hwDevInfo, &devData, pDetail, detailLen, NULL, NULL)) { wcsncpy_s(devName, maxLen, pDetail->DevicePath, _TRUNCATE); - fwprintf(stdout, L"Found device: %s\n", pDetail->DevicePath); } else { diff --git a/src/thunderscope.c b/src/thunderscope.c index 6904762..307290e 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -101,6 +101,8 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) //Get Device Info pInst->identity.device_id = devIdx; pInst->identity.hw_id = litepcie_readl(pInst->ctrl, CSR_DEV_STATUS_HW_ID_ADDR); + pInst->identity.gw_id = litepcie_readl(pInst->ctrl, CSR_DEV_STATUS_GW_REV_ADDR); + pInst->identity.litex = litepcie_readl(pInst->ctrl, CSR_DEV_STATUS_LITEX_REL_ADDR); //Copy device identifier for (uint32_t i = 0; i < TS_IDENT_STR_LEN; i++) { From d781eae7db0baf8ea8f0d20aac051ef503a688f2 Mon Sep 17 00:00:00 2001 From: Aleksa Bjelogrlic Date: Mon, 27 Oct 2025 17:10:01 -0400 Subject: [PATCH 56/83] Changed /std:c++17 to /std:c17 This allows it to compile on windows 11 w/ VS2022 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8859da0..c6054f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON) # error if compiler doesn't support c17 set(CMAKE_C_EXTENSIONS ON) if(MSVC) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /std:c++17 /experimental:c11atomics") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /std:c17 /experimental:c11atomics") endif(MSVC) enable_language(CXX) From 2fcf8287fbadb6bebc266c95d29ced07bb7a1799 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 1 Nov 2025 13:11:21 -0400 Subject: [PATCH 57/83] Fix sleep in MacOS sample read function. --- src/samples.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/samples.c b/src/samples.c index 0b22cde..d2b1fd7 100644 --- a/src/samples.c +++ b/src/samples.c @@ -156,7 +156,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ } else { - NS_DELAY(10000); //10us + usleep(5000); //5ms } #else size_t readLen = bufferLen - retVal; From 62f728aa58b140314f02382c6e715904654e9c7b Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 1 Nov 2025 17:40:20 -0400 Subject: [PATCH 58/83] Reorder when the ADC Frame state machine is reset on channel updates. --- src/adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adc.c b/src/adc.c index f0fc860..1f5bd64 100644 --- a/src/adc.c +++ b/src/adc.c @@ -173,11 +173,11 @@ int32_t ts_adc_update_channels(ts_adc_t* adc) shuffleMode = ((adc->adcDev.width == HMCAD15_8_BIT) ? ADC_8B_SHUFFLE_4CH : ADC_12B_SHUFFLE_4CH); } - litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_DATA_CHANNELS_ADDR, shuffleMode << CSR_ADC_HMCAD1520_DATA_CHANNELS_SHUFFLE_OFFSET); retVal = hmcad15xx_set_channel_config(&adc->adcDev); + litepcie_writel(adc->ctrl, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_FRAME_RST_OFFSET); } return retVal; From 49dd33c3ab80952faa6a71674828586411c0419d Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 29 Oct 2025 00:06:16 -0400 Subject: [PATCH 59/83] factory data updates --- CMakeLists.txt | 43 ++++- include/ts_calibration.h | 47 ++++- src/thunderscope.c | 56 +++++- src/ts_fw_manager.c | 385 +++++++++++++++++++++++++++++++++++++++ src/ts_fw_manager.h | 53 ++++++ 5 files changed, 575 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8859da0..f1acde9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22) +cmake_minimum_required(VERSION 3.22...4.1) project(libtslitex) @@ -19,6 +19,37 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # error if compiler doesn't support c++17 set(CMAKE_CXX_EXTENSIONS ON) +################################ +# Project Dependencies +################################ +include(FetchContent) + +# json-c Library +FetchContent_Declare( + json_c + GIT_REPOSITORY https://github.com/json-c/json-c + GIT_TAG 2372e9518e6ba95b48d37ec162bc7d93b297b52f + FIND_PACKAGE_ARGS NAMES json-c +) + +set(BUILD_SHARED_LIBS OFF) +FetchContent_MakeAvailable(json_c) + +# zlib Library +FetchContent_Declare( + zlib + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG v1.3.1 + FIND_PACKAGE_ARGS NAMES zlib +) + +set(ZLIB_BUILD_TESTING OFF) +set(ZLIB_BUILD_SHARED OFF) +set(ZLIB_INSTALL OFF) +FetchContent_MakeAvailable(zlib) + + + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") @@ -92,7 +123,8 @@ set_target_properties(tslitex PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) -target_link_libraries(tslitex litepcie) +target_link_libraries(tslitex PUBLIC litepcie) +target_link_libraries(tslitex PRIVATE json-c-static zlibstatic) if(APPLE) target_link_libraries(tslitex "-framework IOKit") @@ -112,12 +144,11 @@ set_target_properties(tslitex_static PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) - -#target_compile_definitions(tslitex_static tslitex_static_STATIC) -set_target_properties(tslitex_static PROPERTIES POSITION_INDEPENDENT_CODE 1) +set_target_properties(tslitex_static PROPERTIES POSITION_INDEPENDENT_CODE 1) -target_link_libraries(tslitex_static litepcie) +target_link_libraries(tslitex_static PUBLIC litepcie) +target_link_libraries(tslitex_static PRIVATE json-c-static zlibstatic) target_include_directories(tslitex_static PUBLIC include) file(COPY ${TS_LIB_HEADERS} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/include) diff --git a/include/ts_calibration.h b/include/ts_calibration.h index 2e41bff..0f5e449 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -82,7 +82,7 @@ typedef struct tsScopeCalibration_s * @param channel Channel number * @param cal Channel Calibration data * @return int32_t TS_STATUS_OK if the calibration was accepted -*/ + */ int32_t thunderscopeChanCalibrationSet(tsHandle_t ts, uint32_t channel, tsChannelCalibration_t *cal); /** @@ -132,6 +132,51 @@ int32_t thunderscopeCalibrationManualCtrl(tsHandle_t ts, uint32_t channel, tsCha * @return int32_t TS_STATUS_OK if the parameters were applied */ int32_t thunderscopeCalibrationAdcTest(tsHandle_t ts, tsCalAdcTest_t test_mode, uint32_t test_pattern); + +/****************************************************************************** + * Factory Provisioning API + * WARNING: May cause data corruption + ******************************************************************************/ +/** + * @brief Erase the entire Factory data partition + * + * @param ts Handle to the Thunderscope device + * @param dna Device DNA value for confirmation + * @return int32_t TS_STATUS_OK if the partition is erased successfully + */ +int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna); + +/** + * @brief Append an arbitrary data item to the Factory data partition + * + * @param ts Handle to the Thunderscope device + * @param tag 32-bit Tag to identify the data item + * @param length Length of the content string + * @param content ASCII string containing a JSON object + * @return int32_t TS_STATUS_OK if the FCAL object was written successfully + */ +int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content); + +/** + * @brief Test the Factory data items are valid + * + * @param ts Handle to the Thunderscope device + * @return int32_t TS_STATUS_OK if all data item checks pass + */ +int32_t thunderscopeFactoryProvisionVerify(tsHandle_t ts); + +/** + * @brief Retrieve an item from the Factory data partition. If a NULL buffer is provided, only + * return the length of the item. + * + * @param ts Handle to the Thunderscope device + * @param tag 32-bit Tag value to lookup + * @param content_buffer Pointer for a buffer to store the read value string + * @param item_max_len Maximum size of value string that the provided buffer can hold + * @return int32_t Length of the item value or a negative error code if the Item could not be read + */ +int32_t thunderscopeFactoryReadItem(tsHandle_t ts, const uint32_t tag, char* content_buffer, uint32_t item_max_len); + #ifdef __cplusplus } #endif diff --git a/src/thunderscope.c b/src/thunderscope.c index 307290e..f23a109 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -23,7 +23,7 @@ #include "litepcie.h" -#ifdef _WIN32 +#if defined(_WIN32) #define FILE_FLAGS (FILE_ATTRIBUTE_NORMAL) #else #define FILE_FLAGS (O_RDWR) @@ -402,4 +402,56 @@ int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) { return TS_STATUS_ERROR; } -} \ No newline at end of file +} + +int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_erase(&pInst->fw, dna); + } + else + { + return TS_STATUS_ERROR; + } +} + +int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_append(&pInst->fw, tag, length, content); + } + else + { + return TS_STATUS_ERROR; + } +} + +int32_t thunderscopeFactoryProvisionVerify(tsHandle_t ts) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_verify(&pInst->fw); + } + else + { + return TS_STATUS_ERROR; + } +} + +int32_t thunderscopeFactoryReadItem(tsHandle_t ts, const uint32_t tag, char* content_buffer, uint32_t item_max_len) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + if(pInst) + { + return ts_fw_manager_factory_data_retreive(&pInst->fw, tag, content_buffer, item_max_len); + } + else + { + return TS_STATUS_ERROR; + } +} diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index 4a46d24..f9c9123 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -8,6 +8,16 @@ #include +#if defined(_WIN32) +#include +#pragma comment(lib, "Ws2_32.lib") +#else +#include +#endif + +#include +#include + #include "ts_fw_manager.h" #include "platform.h" #include "spiflash.h" @@ -25,6 +35,8 @@ #define ICAP_REG_IDCODE (0x0C) +#define INVALID_TAG (0xFFFFFFFF) + static const struct { uint32_t code; char name[16]; @@ -465,6 +477,9 @@ int32_t ts_fw_manager_user_data_write(ts_fw_manager_t* mngr, const char* buffer, int32_t ts_fw_manager_factory_cal_get(ts_fw_manager_t* mngr, const char* file_stream, uint32_t len) { // Read File from SPI Flash + // struct json_object * fcal = json_object_new_object(); + + // json_object_object_del(fcal,"root"); return TS_STATUS_OK; } @@ -476,4 +491,374 @@ static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_t { atomic_fetch_add(&mngr->fw_progress, work_done); } +} + +int32_t ts_fw_manager_factory_data_erase(ts_fw_manager_t* mngr, uint64_t dna) +{ + uint64_t dna_actual= litepcie_readl(mngr->flash_dev.fd, CSR_DNA_ID_ADDR); + + if(dna == dna_actual) + { + return spiflash_erase(&mngr->flash_dev, mngr->partition_table->factory_config_start, + (mngr->partition_table->factory_config_end - mngr->partition_table->factory_config_start)); + } + return TS_STATUS_ERROR; +} + +int32_t ts_fw_manager_factory_data_append(ts_fw_manager_t* mngr, uint32_t tag, uint32_t length, const uint8_t *content) +{ + int32_t retVal = TS_STATUS_ERROR; + uint32_t offset = 0; + uint32_t current_tag = INVALID_TAG; + uint32_t next_len = 0; + uint32_t tlv_crc = crc32(0, Z_NULL, 0); + + // Append the new TLV + while (offset < mngr->partition_table->factory_config_end) + { + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), + (uint8_t*)¤t_tag, sizeof(current_tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + if(tag == htonl(current_tag)) + { + LOG_ERROR("ERROR: Duplicate TAG %08X", tag); + retVal = TS_STATUS_ERROR; + break; + } + + // Check if empty + if(current_tag == INVALID_TAG) + { + //Test for length + if((mngr->partition_table->factory_bitstream_start + offset + sizeof(uint32_t)*3 + length) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Not enough space in Factory Partition to store object of %dB",length); + retVal = TS_STATUS_ERROR; + break; + } + + //Write Tag, and Length + uint64_t tl = ((uint64_t)htonl(length) << 32) + htonl(tag); + retVal = spiflash_write(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&tl, sizeof(uint64_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + + offset += sizeof(uint64_t); + + //Write Value + retVal = spiflash_write(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), content, length); + if(retVal < 0) + { + LOG_ERROR("Failed to write value for tag %08x at offset 0x%x", tag, offset); + break; + } + offset += length; + + //Write CRC + tlv_crc = crc32(tlv_crc, content, length); + LOG_DEBUG("Completing TLV with CRC %08X", tlv_crc); + tlv_crc = htonl(tlv_crc); + retVal = spiflash_write(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), (uint8_t*)&tlv_crc, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write CRC32 for tag %08X at offset 0x%x", tag, offset); + break; + } + break; + } + + //Read Len and increment for next tag + offset += sizeof(uint32_t); + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + // Increment size of Length, Value, and CRC + offset += sizeof(uint32_t)*2 + ntohl(next_len); + } + return retVal; +} + +int32_t ts_fw_manager_factory_data_verify(ts_fw_manager_t* mngr) +{ + int32_t retVal = TS_STATUS_OK; + uint32_t offset = 0; + uint32_t tag = INVALID_TAG; + uint32_t next_len = 0; + uint8_t val_buffer[4096]; + uint32_t stored_crc; + uint32_t new_crc = crc32(0, Z_NULL, 0); + uint32_t element_count = 0; + + // Read and Verify each TLV CRC32 + while (offset < mngr->partition_table->factory_config_end) + { + //Read the TAG + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), + (uint8_t*)&tag, sizeof(tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + offset += sizeof(tag); + + // Check if empty, no more tags to verify + if(tag == INVALID_TAG) + { + LOG_DEBUG("Factory data partition verified"); + retVal = TS_STATUS_OK; + break; + } + + //Read the Length + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), + (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + offset += sizeof(uint32_t); + + if((mngr->partition_table->factory_config_start + offset + ntohl(next_len) + sizeof(uint32_t)) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Tag %08X Exceeds Length (%d)", tag, next_len); + retVal = TS_STATUS_ERROR; + break; + } + + //Read the CRC + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset + ntohl(next_len)), + (uint8_t*)&stored_crc, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + + //Read Value and Compute CRC + while(next_len > 0) + { + uint32_t segment_len = next_len > sizeof(val_buffer) ? sizeof(val_buffer) : next_len; + next_len -= segment_len; + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), + val_buffer, segment_len); + if(retVal < 0) + { + LOG_ERROR("Failed to write value for tag %08x at offset 0x%x", tag, offset); + break; + } + offset += segment_len; + + //Calculate the CRC + new_crc = crc32(new_crc, val_buffer, segment_len); + } + + if(ntohl(stored_crc) != new_crc) + { + LOG_ERROR(); + retVal = TS_STATUS_ERROR; + break; + } + + // Increment size of CRC + offset += sizeof(uint32_t); + element_count++; + } + return retVal; +} + +int32_t ts_fw_manager_factory_data_get_length(ts_fw_manager_t* mngr, uint32_t tag) +{ + int32_t retVal = TS_STATUS_OK; + uint32_t offset = 0; + uint32_t read_tag = INVALID_TAG; + uint32_t next_len = 0; + + // Search for a specific Tag and copy it to the provided Content buffer + while (offset < mngr->partition_table->factory_config_end) + { + //Read the TAG + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&read_tag, sizeof(read_tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + offset += sizeof(read_tag); + + // Check if empty, no more tags to try + if(read_tag == INVALID_TAG) + { + LOG_DEBUG("Tag 0x%08X not found in Factory data partition", tag); + retVal = 0; + break; + } + + //Read the Length + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + next_len = ntohl(next_len); + offset += sizeof(uint32_t); + + if((mngr->partition_table->factory_config_start + offset + next_len + sizeof(uint32_t)) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Tag %08X Bad Length (%d)", tag, next_len); + retVal = TS_STATUS_ERROR; + break; + } + + //Tag matches, return length + if(ntohl(read_tag) == tag) + { + retVal = next_len; + break; + } + else + { + offset += (sizeof(uint32_t) + next_len); + } + } + return retVal; +} + +int32_t ts_fw_manager_factory_data_retreive(ts_fw_manager_t* mngr, uint32_t tag, uint8_t* content, uint32_t max_len) +{ + int32_t retVal = TS_STATUS_OK; + uint32_t offset = 0; + uint32_t check_offset = 0; + uint32_t read_tag = INVALID_TAG; + uint32_t next_len = 0; + uint32_t val_len = 0; + uint8_t val_buffer[4096]; + uint32_t stored_crc; + uint32_t new_crc = crc32(0, Z_NULL, 0); + + // Search for a specific Tag and copy it to the provided Content buffer + while (offset < mngr->partition_table->factory_config_end) + { + //Read the TAG + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&read_tag, sizeof(read_tag)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag at offset 0x%x", offset); + break; + } + + offset += sizeof(read_tag); + + // Check if empty, no more tags to try + if(tag == INVALID_TAG) + { + LOG_DEBUG("Tag 0x%08X not found in Factory data partition", tag); + retVal = TS_STATUS_OK; + break; + } + + //Read the Length + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset), (uint8_t*)&next_len, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to read tag %08x length at offset 0x%x", ntohl(read_tag), offset); + break; + } + next_len = ntohl(next_len); + offset += sizeof(uint32_t); + + if((mngr->partition_table->factory_config_start + offset + next_len + sizeof(uint32_t)) > + mngr->partition_table->factory_config_end) + { + LOG_ERROR("Tag %08X Bad Length (%d)", ntohl(read_tag), next_len); + retVal = TS_STATUS_ERROR; + break; + } + + //Find next if tag doesn't match + if(ntohl(read_tag) != tag) + { + offset += (sizeof(uint32_t) + next_len); + continue; + } + + //Read the CRC + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start+offset + next_len), (uint8_t*)&stored_crc, sizeof(uint32_t)); + if(retVal < 0) + { + LOG_ERROR("Failed to write tag %08x at offset 0x%x", tag, offset); + break; + } + + //Read Value and Compute CRC + val_len = next_len; + check_offset = offset; + while(next_len > 0) + { + uint32_t segment_len = next_len > sizeof(val_buffer) ? sizeof(val_buffer) : next_len; + next_len -= segment_len; + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + check_offset), val_buffer, segment_len); + if(retVal < 0) + { + LOG_ERROR("Failed to read value for tag %08x at offset 0x%x : %d", tag, offset, retVal); + break; + } + check_offset += segment_len; + + //Calculate the CRC + new_crc = crc32(new_crc, val_buffer, segment_len); + } + + if(ntohl(stored_crc) != new_crc) + { + LOG_ERROR("Failed to read Tag %08X, bad CRC (expected %08X, calculated %08X)", tag, + ntohl(stored_crc), new_crc); + retVal = TS_STATUS_ERROR; + break; + } + + //CRC Passes, read to user buffer + new_crc = crc32(0, Z_NULL, 0); + retVal = spiflash_read(&mngr->flash_dev, (mngr->partition_table->factory_config_start + offset), content, val_len); + if(retVal < 0) + { + LOG_ERROR("Failed to read value for tag %08x at offset 0x%x : %d", tag, offset, retVal); + break; + } + + //Calculate the CRC one last time + new_crc = crc32(new_crc, content, val_len); + + if(ntohl(stored_crc) == new_crc) + { + retVal = val_len; + } + else + { + LOG_ERROR("Failed CRC check for tag %08X (expected 0x%08X, calculated 0x%08X)", + tag, ntohl(stored_crc), new_crc); + retVal = TS_STATUS_ERROR; + } + break; + } + return retVal; } \ No newline at end of file diff --git a/src/ts_fw_manager.h b/src/ts_fw_manager.h index 6965ee4..10bfdd2 100644 --- a/src/ts_fw_manager.h +++ b/src/ts_fw_manager.h @@ -89,6 +89,59 @@ int32_t ts_fw_manager_user_data_read(ts_fw_manager_t* mngr, char* buffer, uint32 */ int32_t ts_fw_manager_factory_reset(ts_fw_manager_t* mngr, bool reset_config, bool reset_bitstream); +/** + * @brief Erase the Factory Data partition + * + * @param mngr Pointer to a manager instance + * @param dna Unit PORT_DNA value + * + * @return int32_t TS_STATUS_SUCCESS if the factory partition is erased. + */ +int32_t ts_fw_manager_factory_data_erase(ts_fw_manager_t* mngr, uint64_t dna); + +/** + * @brief Append a TLV to the Factory data + * + * @param mngr Pointer to a manager instance + * @param tag 32-bit Tag + * @param length Length of the value data + * @param content Pointer to the TLV value data array + * + * @return + */ +int32_t ts_fw_manager_factory_data_append(ts_fw_manager_t* mngr, uint32_t tag, uint32_t length, const uint8_t *content); + + +/** + * @brief Retrieve the length of a specific Tag from the Factory data partition + * + * @param mngr Pointer to a manager instance + * @param tag Tag to retrieve the length of + * + * @return Length of the item if successful, zero if the item is not found, else TS_STATUS_ERROR + */ +int32_t ts_fw_manager_factory_data_get_length(ts_fw_manager_t* mngr, uint32_t tag); + +/** + * @brief Read every item and confirm good checksum for each value + * + * @param mngr Pointer to a manager instance + * + * @return TS_STATUS_OK if every item has a good checksum + */ +int32_t ts_fw_manager_factory_data_verify(ts_fw_manager_t* mngr); + +/** + * @brief Read a specific Tag from the Factory data partition + * + * @param mngr Pointer to a manager instance + * @param tag Tag to retrieve + * @param content Pointer to a buffer to store the TLV value + * @param max_len Not-to-exceed length of the content buffer + * + * @return Length of the content retrieved if successful, else TS_STATUS_ERROR + */ +int32_t ts_fw_manager_factory_data_retreive(ts_fw_manager_t* mngr, uint32_t tag, uint8_t* content, uint32_t max_len); #ifdef __cplusplus } From 71d973ed3478ffc98230fdeb1df202c14b0dad73 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 6 Nov 2025 00:46:10 -0500 Subject: [PATCH 60/83] Add parsing of HWID from factory data partition. --- CMakeLists.txt | 12 ++- bindings/python/tslitex.pxd | 4 + doc/calibration_data.md | 37 +++++++++ example/thunderscope_fw.cpp | 87 ++++++++++++++++++++ include/ts_calibration.h | 2 +- include/ts_common.h | 17 ++-- src/thunderscope.c | 28 ++++++- src/ts_data.c | 156 ++++++++++++++++++++++++++++++++++++ src/ts_data.h | 50 ++++++++++++ src/ts_fw_manager.c | 15 +--- 10 files changed, 383 insertions(+), 25 deletions(-) create mode 100644 doc/calibration_data.md create mode 100644 src/ts_data.c create mode 100644 src/ts_data.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f1acde9..af3e872 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,9 +53,15 @@ FetchContent_MakeAvailable(zlib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts/libtslitex") -add_definitions(-DUNICODE -D_UNICODE) +option(ENABLE_FACTORY_PROVISIONING "Include Factory Provisioning Functions" OFF) -add_definitions(-DEN_LOGGING) +if(ENABLE_FACTORY_PROVISIONING) + add_compile_definitions(FACTORY_PROVISIONING_API) +endif() + +add_compile_definitions(UNICODE _UNICODE) + +add_compile_definitions(EN_LOGGING) add_compile_definitions("$<$:EN_LOGGING_DEBUG>") @@ -80,6 +86,7 @@ set(TS_SOURCES src/samples.c src/spiflash.c src/ts_channel.c + src/ts_data.c src/ts_fw_manager.c src/thunderscope.c ) @@ -100,6 +107,7 @@ set(TS_HEADERS src/samples.h src/spiflash.h src/ts_channel.h + src/ts_data.h src/ts_fw_manager.h src/util.h ) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 3f23899..27202df 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -37,9 +37,13 @@ cdef extern from "thunderscope.h": uint32_t hw_id uint32_t gw_id uint32_t litex + uint32_t board_rev char device_path[256] char identity[256] char serial_number[256] + char build_config[256] + char build_date[256] + char mfg_signature[256] ctypedef tsDeviceInfo_s tsDeviceInfo_t diff --git a/doc/calibration_data.md b/doc/calibration_data.md new file mode 100644 index 0000000..20e79b6 --- /dev/null +++ b/doc/calibration_data.md @@ -0,0 +1,37 @@ +## Calibration Data + +### API + +- Factory Provision Prepare +- Factory Provision Append Data +- Factory Provision Verify +- Factory Read Item + + +### Factory TLV's + +Each section is dentoted by a 32-bit TAG, 32-bit LENGTH, ASCII Data, and CRC32 Checksum. + +#### HWID + +- Tag: `HWID` +- Format: JSON +- Contents: + - ID Version + - "Serial Number" + - "Board Revision" + - "Build Config" + - "Build Date" + - "Mfg Signature" + + +#### FCAL + +- Tag: `FCAL` +- Format: JSON +- Contents: + - Calibration Version + - Calibration Station + - Calibration Date + - Calibration Data + diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 6a6a5ad..e87034b 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -22,6 +22,7 @@ #include "optparse.h" #include +#include #define MAX_USER_DATA_SIZE (0x500000) @@ -142,6 +143,13 @@ static void print_help(void) printf("\thelp - Print this help message\r\n"); printf("\tCommon Options:\r\n"); printf("\t\t-d Device Index\r\n"); +#if defined(FACTORY_PROVISIONING_API) + printf("--FACTORY USAGE ONLY--\r\n"); + printf("\tfactory_prepare [dna] - Erase the Factory Data Partition\r\n"); + printf("\t\t[dna] - Device DNA value to confirm erase\r\n"); + printf("\tfactory_load [tag] [file] - Load a JSON file with Factory Data\r\n"); + printf("\t\t[tag] - Tag identifier for the data item\r\n"); +#endif } int main(int argc, char** argv) @@ -258,6 +266,85 @@ int main(int argc, char** argv) //Nothing else to do ;; } +#if defined(FACTORY_PROVISIONING_API) + else if(0 == strcmp(arg, "factory_prepare")) + { + if(argc != 3) + { + printf("Error: Missing args\r\n"); + print_help(); + } + else + { + uint64_t op_dna = 0; + if(!sscanf(argv[2], "%llx", &op_dna)) + { + printf("Error: Invalid DNA\r\n"); + print_help(); + } + else if(TS_STATUS_OK == thunderscopeFactoryProvisionPrepare(ts, op_dna)) + { + printf("Factory Data Partition Erased \r\n"); + } + else + { + printf("Failed to prepare Factory Data Partition\r\n"); + } + } + } + else if(0 == strcmp(arg, "factory_load")) + { + char tag[5]; + if(argc != 4) + { + printf("Error: Missing args\r\n"); + print_help(); + } + else if(!sscanf(argv[2], "%4s", tag)) + { + printf("Error: Invalid TAG arg\r\n"); + print_help(); + } + else + { + file_path = argv[3]; + std::error_code err; + auto file_size = std::filesystem::file_size(file_path, err); + if(err) + { + std::cout << "Error opening \"" << file_path << "\" : " << err.message() << "\r\n"; + thunderscopeClose(ts); + return -1; + } + + // Open Data File + std::ifstream file(file_path, std::ios::binary); + if(file) + { + char* data_content = new char[file_size]; + file.read(data_content, file_size); + + if(TS_STATUS_OK == thunderscopeFactoryProvisionAppendTLV(ts, (uint32_t)(tag[0] + (tag[1] << 8) + (tag[2] << 16) + (tag[3] << 24)), + file_size, (const char*)data_content)) + { + printf("Finished writing the %s item\r\n", tag); + } + else + { + printf("Failed to write factory data item\r\n"); + } + + // Close File + file.close(); + delete data_content; + } + else + { + printf("ERROR Factory TLV: Failed to open file %s\r\n", file_path); + } + } + } +#endif else if(argCount == argc) { printf("**Missing File path argument**\r\n"); diff --git a/include/ts_calibration.h b/include/ts_calibration.h index 0f5e449..ae89161 100644 --- a/include/ts_calibration.h +++ b/include/ts_calibration.h @@ -153,7 +153,7 @@ int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna); * @param tag 32-bit Tag to identify the data item * @param length Length of the content string * @param content ASCII string containing a JSON object - * @return int32_t TS_STATUS_OK if the FCAL object was written successfully + * @return int32_t TS_STATUS_OK if the object was written successfully */ int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content); diff --git a/include/ts_common.h b/include/ts_common.h index 3d76558..5a77536 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -61,12 +61,17 @@ typedef enum tsChannelTerm_e typedef struct tsDeviceInfo_s { uint32_t device_id; - uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ - uint32_t gw_id; /**< 32-bit version ID: gw_id[31:16] - Major, gw_id[15:8] - Minor, gw_id[7:1] - Patch, gw_id[0] - next */ - uint32_t litex; /**< 32-bit LiteX version: litex[31:16] - Year, litex[15:0] - Month */ - char device_path[TS_IDENT_STR_LEN]; - char identity[TS_IDENT_STR_LEN]; - char serial_number[TS_IDENT_STR_LEN]; + uint32_t hw_id; /**< hw_id[9] - ID Valid, hw_id[8] - PCIe/USB, hw_id[7:4] - Reserved, hw_id[3:0] - Revision */ + uint32_t gw_id; /**< 32-bit version ID: gw_id[31:16] - Major, gw_id[15:8] - Minor, gw_id[7:1] - Patch, gw_id[0] - next */ + uint32_t litex; /**< 32-bit LiteX version: litex[31:16] - Year, litex[15:0] - Month */ + uint32_t board_rev; /**< 32-bit Board revision counter. */ + char device_path[TS_IDENT_STR_LEN]; /**< Device Path where the instance is opened */ + char identity[TS_IDENT_STR_LEN]; /**< Device Identity String */ + char serial_number[TS_IDENT_STR_LEN]; /**< Serial Number */ + char build_config[TS_IDENT_STR_LEN]; /**< Build Configuration description */ + char build_date[TS_IDENT_STR_LEN]; /**< Date of Manufacture */ + char mfg_signature[TS_IDENT_STR_LEN]; /**< Device Unique Manufacturing Signature */ + } tsDeviceInfo_t; typedef struct tsChannelParam_s diff --git a/src/thunderscope.c b/src/thunderscope.c index f23a109..e0200ae 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -47,6 +47,7 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) { int32_t retVal = TS_STATUS_ERROR; char testPath[TS_IDENT_STR_LEN]; + ts_fw_manager_t fwmngr; // Find device path by index snprintf(testPath, TS_IDENT_STR_LEN, LITEPCIE_CTRL_NAME(%d), devIndex); @@ -64,9 +65,11 @@ int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t *info) { info->identity[i] = (char)litepcie_readl(testDev, CSR_IDENTIFIER_MEM_BASE + 4 * i); } - //TODO Implement Serial Number - snprintf(info->serial_number, TS_IDENT_STR_LEN, "TS00##"); strncpy(info->device_path, testPath, TS_IDENT_STR_LEN); + // Get Factory Build Info + ts_fw_manager_init(testDev, &fwmngr); + ts_data_factory_id_get(&fwmngr, info); + litepcie_close(testDev); retVal = TS_STATUS_OK; } @@ -108,8 +111,8 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) { pInst->identity.identity[i] = (char)litepcie_readl(pInst->ctrl, CSR_IDENTIFIER_MEM_BASE + 4 * i); } - //TODO Implement Serial Number - snprintf(pInst->identity.serial_number, TS_IDENT_STR_LEN, "TS00##"); + + strncpy(pInst->identity.device_path, devName, TS_IDENT_STR_LEN); if(!skip_init) @@ -143,6 +146,9 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) return NULL; } + // Get Factory Build Info + ts_data_factory_id_get(&pInst->fw, &pInst->identity); + pInst->status_leds.fd = pInst->ctrl; pInst->status_leds.reg = TS_STATUS_LED_ADDR; pInst->status_leds.bit_mask = TS_STATUS_LED_MASK; @@ -404,6 +410,7 @@ int32_t thunderscopeGetFwProgress(tsHandle_t ts, uint32_t* progress) } } +#if defined(FACTORY_PROVISIONING_API) int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna) { ts_inst_t* pInst = (ts_inst_t*)ts; @@ -429,6 +436,19 @@ int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, return TS_STATUS_ERROR; } } +#else +int32_t thunderscopeFactoryProvisionPrepare(tsHandle_t ts, uint64_t dna) +{ + LOG_ERROR("Factory API Not Enabled"); + return TS_STATUS_ERROR; +} + +int32_t thunderscopeFactoryProvisionAppendTLV(tsHandle_t ts, const uint32_t tag, uint32_t length, const char* content) +{ + LOG_ERROR("Factory API Not Enabled"); + return TS_STATUS_ERROR; +} +#endif int32_t thunderscopeFactoryProvisionVerify(tsHandle_t ts) { diff --git a/src/ts_data.c b/src/ts_data.c new file mode 100644 index 0000000..d7effad --- /dev/null +++ b/src/ts_data.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of libtslitex. + * Provide methods to fetch and store content in flash + * + * Copyright (c) 2024 Nate Meyer + */ + +#include +#include + +#include "ts_data.h" +#include "ts_fw_manager.h" +#include "util.h" + + +int32_t ts_data_factory_cal_get(ts_fw_manager_t* mngr, tsScopeCalibration_t *fcal) +{ + // struct json_object * fcal; + + return TS_STATUS_OK; +} + +int32_t ts_data_factory_id_get(ts_fw_manager_t* mngr, tsDeviceInfo_t* infos) +{ + struct json_object *fid; + struct json_object *item; + uint32_t hwid_version = 0; + uint32_t hwid_len = 0; + uint8_t* hwid_buffer = NULL; + + // Check HWID tag in factory partition + hwid_len = ts_fw_manager_factory_data_get_length(mngr, TAG_HWID); + + if(hwid_len <= 0) + { + LOG_ERROR("Could not read mfg data"); + return TS_STATUS_ERROR; + } + + // Read HWID to buffer + hwid_buffer = malloc(hwid_len + 1); + if(TS_STATUS_ERROR == ts_fw_manager_factory_data_retreive(mngr, TAG_HWID, hwid_buffer, hwid_len)) + { + LOG_ERROR("Failed to read HWID"); + free(hwid_buffer); + return TS_STATUS_ERROR; + } + + // Parse json + hwid_buffer[hwid_len] = '\0'; + fid = json_tokener_parse(hwid_buffer); + + // Store HWID version + if(json_object_object_get_ex(fid,"version", &item)) + { + if(json_object_get_type(item) == json_type_int) + { + hwid_version = json_object_get_int(item); + } + else + { + LOG_ERROR("HWID Version bad value"); + } + } + else + { + LOG_ERROR("HWID Version key not found"); + } + + // Fill struct with info + if(json_object_object_get_ex(fid,"Serial Number", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->serial_number, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Serial Number bad value"); + } + } + else + { + LOG_ERROR("Serial Number key not found"); + } + + if(json_object_object_get_ex(fid,"Board Revision", &item)) + { + if(json_object_get_type(item) == json_type_int) + { + infos->board_rev = json_object_get_int(item); + } + else + { + LOG_ERROR("Board Revision bad value"); + } + } + else + { + LOG_ERROR("Board Revision key not found"); + } + + if(json_object_object_get_ex(fid,"Build Config", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->build_config, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Build Config bad value"); + } + } + else + { + LOG_ERROR("Build Config key not found"); + } + + // Build Date + if(json_object_object_get_ex(fid,"Build Date", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->build_date, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Build Date bad value"); + } + } + else + { + LOG_ERROR("Build Date key not found"); + } + + // Signature + if(json_object_object_get_ex(fid,"Mfg Signature", &item)) + { + if(json_object_get_type(item) == json_type_string) + { + strncpy(infos->mfg_signature, json_object_get_string(item), TS_IDENT_STR_LEN); + } + else + { + LOG_ERROR("Mfg Signature bad value"); + } + } + else + { + LOG_ERROR("Mfg Signature key not found"); + } + + free(hwid_buffer); + return TS_STATUS_OK; +} diff --git a/src/ts_data.h b/src/ts_data.h new file mode 100644 index 0000000..87a2e4c --- /dev/null +++ b/src/ts_data.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of libtslitex. + * Library configuration definitions + * + * Copyright (C) 2025 / Nate Meyer / nate.devel@gmail.com + * + */ +#ifndef _TS_DATA_H_ +#define _TS_DATA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ts_common.h" +#include "ts_calibration.h" +#include "ts_fw_manager.h" + + +#define TAGSTR(x) (uint32_t)(x[0] + (x[1] << 8) + (x[2] << 16) + (x[3] << 24)) +#define TAG_HWID TAGSTR("HWID") +#define TAG_FCAL TAGSTR("FCAL") + +/** + * @brief Read and parse the Factory Calibration from flash + * + * @param mngr Pointer to the FW Manager instance + * @param fcal Pointer to the calibration data struct + * + * @return TS_STATUS_OK if the calibration was parsed successfully + */ +int32_t ts_data_factory_cal_get(ts_fw_manager_t* mngr, tsScopeCalibration_t *fcal); + +/** + * @brief Read and parse the Factory Device Information + * + * @param mngr Pointer to the FW Manager instance + * @param infos Pointer to the device information structf + * + * @return TS_STATUS_OK if the Device information data was parsed + */ +int32_t ts_data_factory_id_get(ts_fw_manager_t* mngr, tsDeviceInfo_t* infos); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/ts_fw_manager.c b/src/ts_fw_manager.c index f9c9123..0c5e542 100644 --- a/src/ts_fw_manager.c +++ b/src/ts_fw_manager.c @@ -15,7 +15,6 @@ #include #endif -#include #include #include "ts_fw_manager.h" @@ -474,16 +473,6 @@ int32_t ts_fw_manager_user_data_write(ts_fw_manager_t* mngr, const char* buffer, return TS_STATUS_OK; } -int32_t ts_fw_manager_factory_cal_get(ts_fw_manager_t* mngr, const char* file_stream, uint32_t len) -{ - // Read File from SPI Flash - // struct json_object * fcal = json_object_new_object(); - - // json_object_object_del(fcal,"root"); - - return TS_STATUS_OK; -} - static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_total) { ts_fw_manager_t* mngr = (ts_fw_manager_t*)ctx; @@ -495,7 +484,8 @@ static void ts_fw_progress_update(void* ctx, uint32_t work_done, uint32_t work_t int32_t ts_fw_manager_factory_data_erase(ts_fw_manager_t* mngr, uint64_t dna) { - uint64_t dna_actual= litepcie_readl(mngr->flash_dev.fd, CSR_DNA_ID_ADDR); + uint64_t dna_actual= (uint64_t)litepcie_readl(mngr->flash_dev.fd, CSR_DNA_ID_ADDR) << 32; + dna_actual |= litepcie_readl(mngr->flash_dev.fd, CSR_DNA_ID_ADDR + 4); if(dna == dna_actual) { @@ -573,6 +563,7 @@ int32_t ts_fw_manager_factory_data_append(ts_fw_manager_t* mngr, uint32_t tag, u LOG_ERROR("Failed to write CRC32 for tag %08X at offset 0x%x", tag, offset); break; } + retVal = TS_STATUS_OK; break; } From 8d13efa5aeae5baf87263112f51e017b71d18ff1 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 6 Nov 2025 00:48:34 -0500 Subject: [PATCH 61/83] Add hwid schema --- doc/data/hwid.schema.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 doc/data/hwid.schema.json diff --git a/doc/data/hwid.schema.json b/doc/data/hwid.schema.json new file mode 100644 index 0000000..938bab6 --- /dev/null +++ b/doc/data/hwid.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Hardware Identity Description", + "type": "object", + "properties": { + "version": { + "type": "integer", + "minimum": 1 + }, + "Serial Number": { + "type": "string", + "maxLength": 256 + }, + "Board Revision": { + "type": "number" + }, + "Build Config": { + "type": "string", + "maxLength": 256 + }, + "Build Date": { + "type": "string", + "maxLength": 256 + }, + "Mfg Signature": { + "type": "string", + "maxLength": 256 + } + }, + "required": [ + "version", + "Serial Number" + ] +} \ No newline at end of file From e27a58bdf68488a8514dae5f4bf8048af2a0b842 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 6 Nov 2025 23:27:17 -0500 Subject: [PATCH 62/83] Update DMA ioctl api --- litepcie/public_h/litepcie_linux.h | 2 ++ litepcie/public_h/litepcie_mac.h | 2 ++ litepcie/public_h/litepcie_win.h | 2 ++ litepcie/src/litepcie_dma.c | 2 ++ 4 files changed, 8 insertions(+) diff --git a/litepcie/public_h/litepcie_linux.h b/litepcie/public_h/litepcie_linux.h index d284165..96ee48f 100644 --- a/litepcie/public_h/litepcie_linux.h +++ b/litepcie/public_h/litepcie_linux.h @@ -39,6 +39,7 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; @@ -46,6 +47,7 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/litepcie/public_h/litepcie_mac.h b/litepcie/public_h/litepcie_mac.h index 8985072..f4da061 100644 --- a/litepcie/public_h/litepcie_mac.h +++ b/litepcie/public_h/litepcie_mac.h @@ -24,6 +24,7 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint32_t enable; + uint32_t interrupt_count; uint32_t channel; int64_t hw_count; int64_t sw_count; @@ -32,6 +33,7 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint32_t enable; + uint32_t interrupt_count; uint32_t channel; int64_t hw_count; int64_t sw_count; diff --git a/litepcie/public_h/litepcie_win.h b/litepcie/public_h/litepcie_win.h index 47bec48..04d3130 100644 --- a/litepcie/public_h/litepcie_win.h +++ b/litepcie/public_h/litepcie_win.h @@ -53,6 +53,7 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; @@ -60,6 +61,7 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint8_t enable; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index 1469fd6..fb806e2 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -42,6 +42,7 @@ void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_e void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_writer m; m.enable = enable; + m.interrupt_count = DMA_BUFFER_PER_IRQ; #if defined(__APPLE__) size_t outlen = sizeof(m); m.channel = dma->channel; @@ -58,6 +59,7 @@ void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_reader m; m.enable = enable; + m.interrupt_count = DMA_BUFFER_PER_IRQ; #if defined(__APPLE__) size_t outlen = sizeof(m); m.channel = dma->channel; From 0c53180c1be1eec4df455fc19aea565d0e74f0d2 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 8 Nov 2025 03:20:05 -0500 Subject: [PATCH 63/83] Add sample interrupt control --- include/thunderscope.h | 14 +++++++++++++ litepcie/include/litepcie_dma.h | 4 ++-- litepcie/src/litepcie_dma.c | 16 +++++++-------- src/samples.c | 5 ++++- src/samples.h | 1 + src/thunderscope.c | 35 ++++++++++++++++++++++++++++++++- 6 files changed, 63 insertions(+), 12 deletions(-) diff --git a/include/thunderscope.h b/include/thunderscope.h index abeb5fe..896253c 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -86,6 +86,20 @@ int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* conf); */ int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolution); +/** + * @brief Set the approximate rate at which interrupts will fire + * + * This method sets the target frequency for interrupts in the driver. A higher value will trigger + * more often, zero is invalid. The default update rate is 100Hz, or every 10ms. The driver + * configuration will be updated as the sample mode is changed. This setting will take affect the + * next time the ADC is enabled. + * + * @param ts Handle to the Thunderscope device + * @param rate Sample Interrupt Rate (interrupts per second) + * @return int32_t TS_STATUS_OK if the Thunderscope was configured + */ +int32_t thunderscopeSampleInterruptRate(tsHandle_t ts, uint32_t interrupt_rate); + /** * @brief Enable or Disable * diff --git a/litepcie/include/litepcie_dma.h b/litepcie/include/litepcie_dma.h index ff66d91..c5baf43 100644 --- a/litepcie/include/litepcie_dma.h +++ b/litepcie/include/litepcie_dma.h @@ -42,8 +42,8 @@ struct litepcie_dma_ctrl { }; void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_enable); -void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); -void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); +void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); +void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count); uint8_t litepcie_request_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer); void litepcie_release_dma(struct litepcie_dma_ctrl *dma, uint8_t reader, uint8_t writer); diff --git a/litepcie/src/litepcie_dma.c b/litepcie/src/litepcie_dma.c index fb806e2..bc31d49 100644 --- a/litepcie/src/litepcie_dma.c +++ b/litepcie/src/litepcie_dma.c @@ -39,10 +39,10 @@ void litepcie_dma_set_loopback(struct litepcie_dma_ctrl *dma, uint8_t loopback_e checked_ioctl(ioctl_args(dma->fds.fd, LITEPCIE_IOCTL_DMA, m)); } -void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { +void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_writer m; m.enable = enable; - m.interrupt_count = DMA_BUFFER_PER_IRQ; + m.interrupt_count = intr_count; #if defined(__APPLE__) size_t outlen = sizeof(m); m.channel = dma->channel; @@ -56,10 +56,10 @@ void litepcie_dma_writer(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *lost_count = m.lost_count; } -void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { +void litepcie_dma_reader(struct litepcie_dma_ctrl *dma, uint8_t enable, uint32_t intr_count, int64_t *hw_count, int64_t *sw_count, int64_t *lost_count) { struct litepcie_ioctl_dma_reader m; m.enable = enable; - m.interrupt_count = DMA_BUFFER_PER_IRQ; + m.interrupt_count = intr_count; #if defined(__APPLE__) size_t outlen = sizeof(m); m.channel = dma->channel; @@ -195,9 +195,9 @@ int litepcie_dma_init(struct litepcie_dma_ctrl *dma, const char *device_name, ui void litepcie_dma_cleanup(struct litepcie_dma_ctrl *dma) { if (dma->use_reader) - litepcie_dma_reader(dma, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); + litepcie_dma_reader(dma, 0, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); if (dma->use_writer) - litepcie_dma_writer(dma, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); + litepcie_dma_writer(dma, 0, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); litepcie_release_dma(dma, dma->use_reader, dma->use_writer); @@ -223,9 +223,9 @@ void litepcie_dma_process(struct litepcie_dma_ctrl *dma) /* set / get dma */ if (dma->use_writer) - litepcie_dma_writer(dma, 1, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); + litepcie_dma_writer(dma, 1, 0, &dma->writer_hw_count, &dma->writer_sw_count, &dma->writer_dropped_count); if (dma->use_reader) - litepcie_dma_reader(dma, 1, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); + litepcie_dma_reader(dma, 1, 0, &dma->reader_hw_count, &dma->reader_sw_count, &dma->reader_dropped_count); #if defined(_WIN32) uint32_t retLen = 0; diff --git a/src/samples.c b/src/samples.c index d2b1fd7..5843341 100644 --- a/src/samples.c +++ b/src/samples.c @@ -101,6 +101,7 @@ int32_t samples_enable_set(sampleStream_t* inst, uint8_t en) //Start/Stop DMA litepcie_dma_writer(&inst->dma, en, + inst->interrupt_rate, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -139,6 +140,7 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ { #if defined (APPLE_MMAP_DMA) //POLLING litepcie_dma_writer(&inst->dma, inst->active, + inst->interrupt_rate, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -201,6 +203,7 @@ int32_t samples_update_status(sampleStream_t* inst) } litepcie_dma_writer(&inst->dma, inst->active, + inst->interrupt_rate, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); @@ -218,7 +221,7 @@ int32_t samples_teardown(sampleStream_t* inst) else { inst->active = 0; - litepcie_dma_writer(&inst->dma, 0, + litepcie_dma_writer(&inst->dma, 0, 0, &inst->dma_buffer_count, &inst->driver_buffer_count, &inst->dropped_buffer_count); diff --git a/src/samples.h b/src/samples.h index 18a6db7..a53271c 100644 --- a/src/samples.h +++ b/src/samples.h @@ -31,6 +31,7 @@ typedef struct sampleStream_s int64_t dma_buffer_count; int64_t driver_buffer_count; int64_t dropped_buffer_count; + uint32_t interrupt_rate; uint8_t active; } sampleStream_t; diff --git a/src/thunderscope.c b/src/thunderscope.c index e0200ae..614bc85 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -29,6 +29,8 @@ #define FILE_FLAGS (O_RDWR) #endif +#define DEFAULT_DATA_INTR_RATE (100) + typedef struct ts_inst_s { file_t ctrl; @@ -39,6 +41,7 @@ typedef struct ts_inst_s const led_signals_t *signals; bool initialized; ts_fw_manager_t fw; + uint32_t interrupt_rate; //TBD - Other Instance Data } ts_inst_t; @@ -117,6 +120,7 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) if(!skip_init) { + pInst->interrupt_rate = DEFAULT_DATA_INTR_RATE; if(TS_STATUS_OK != ts_channel_init(&pInst->pChannel, pInst->ctrl)) { LOG_ERROR("Failed to initialize channels"); @@ -232,7 +236,36 @@ int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolut if(pInst && pInst->initialized) { - return ts_channel_sample_rate_set(pInst->pChannel, rate, resolution); + int32_t status = ts_channel_sample_rate_set(pInst->pChannel, rate, resolution); + if(status == TS_STATUS_OK) + { + //Target 100Hz interrupt rate + pInst->samples.interrupt_rate = 1 + ((((resolution == 256) ? rate : 2*rate) / + (DMA_BUFFER_SIZE)) / pInst->interrupt_rate); + + LOG_DEBUG("DMA Interrupt Rate is 1/%d MB", pInst->samples.interrupt_rate); + } + return status; + } + + return TS_STATUS_ERROR; +} + +int32_t thunderscopeSampleInterruptRate(tsHandle_t ts, uint32_t interrupt_rate) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + if(interrupt_rate == 0) + { + interrupt_rate = DEFAULT_DATA_INTR_RATE; + } + pInst->interrupt_rate = interrupt_rate; + + LOG_DEBUG("Targeting Interrupt frequency of %d Hz", pInst->interrupt_rate); + + return TS_STATUS_OK; } return TS_STATUS_ERROR; From c1c927764b84eeff87698e3c7be30ac5d7c2eba5 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 8 Nov 2025 17:17:27 -0500 Subject: [PATCH 64/83] Fix build issues and warnings on macOS --- CMakeLists.txt | 2 +- example/thunderscope_fw.cpp | 6 +++--- litepcie/public_h/litepcie_mac.h | 4 ++-- src/afe.c | 2 +- src/samples.c | 4 ++-- src/thunderscope.c | 1 + 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index af3e872..91f700b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ target_link_libraries(tslitex PUBLIC litepcie) target_link_libraries(tslitex PRIVATE json-c-static zlibstatic) if(APPLE) - target_link_libraries(tslitex "-framework IOKit") + target_link_libraries(tslitex PRIVATE "-framework IOKit") endif() target_include_directories(tslitex PUBLIC include) diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index e87034b..8fc700d 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -53,7 +53,7 @@ static void user_write(tsHandle_t ts, const char* file_path, uint32_t offset) // Close File file.close(); - delete bitstream; + delete[] bitstream; } else { @@ -81,7 +81,7 @@ static void user_read(tsHandle_t ts, const char* file_path) // Close File file.flush(); file.close(); - delete data_buffer; + delete[] data_buffer; } else { @@ -115,7 +115,7 @@ static void fw_upgrade(tsHandle_t ts, const char* file_path) // Close File file.close(); - delete bitstream; + delete[] bitstream; } else { diff --git a/litepcie/public_h/litepcie_mac.h b/litepcie/public_h/litepcie_mac.h index f4da061..028e59f 100644 --- a/litepcie/public_h/litepcie_mac.h +++ b/litepcie/public_h/litepcie_mac.h @@ -24,8 +24,8 @@ struct litepcie_ioctl_dma { struct litepcie_ioctl_dma_writer { uint32_t enable; - uint32_t interrupt_count; uint32_t channel; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; @@ -33,8 +33,8 @@ struct litepcie_ioctl_dma_writer { struct litepcie_ioctl_dma_reader { uint32_t enable; - uint32_t interrupt_count; uint32_t channel; + uint32_t interrupt_count; int64_t hw_count; int64_t sw_count; int64_t lost_count; diff --git a/src/afe.c b/src/afe.c index bb4bc39..31a019a 100644 --- a/src/afe.c +++ b/src/afe.c @@ -52,7 +52,7 @@ int32_t ts_afe_init(ts_afe_t* afe, uint8_t channel, spi_dev_t afe_amp, i2c_t tri { afe->trimPotBits = MCP4452_NUM_BITS; - if(litepcie_readl(coupling.fd, CSR_DEV_STATUS_HW_ID_ADDR) & TS_HW_ID_REV_MASK > 0) + if((litepcie_readl(coupling.fd, CSR_DEV_STATUS_HW_ID_ADDR) & TS_HW_ID_REV_MASK) > 0) { afe->couplingInverted = true; } diff --git a/src/samples.c b/src/samples.c index 5843341..a6a6aaf 100644 --- a/src/samples.c +++ b/src/samples.c @@ -145,9 +145,9 @@ int32_t samples_get_buffers(sampleStream_t* inst, uint8_t* sampleBuffer, uint32_ &inst->driver_buffer_count, &inst->dropped_buffer_count); int64_t buff_available = inst->dma_buffer_count - inst->driver_buffer_count; - if(buff_available > (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ)) + if(buff_available > (DMA_BUFFER_COUNT - inst->interrupt_rate)) { - inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - DMA_BUFFER_PER_IRQ); + inst->driver_buffer_count = inst->dma_buffer_count - (DMA_BUFFER_COUNT - inst->interrupt_rate); inst->dropped_buffer_count++; } if(buff_available > 0) diff --git a/src/thunderscope.c b/src/thunderscope.c index 614bc85..bafd8b3 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -18,6 +18,7 @@ #include "samples.h" #include "gpio.h" #include "ts_fw_manager.h" +#include "ts_data.h" #include "util.h" #include "litepcie.h" From 793fe2edd08dcb1023f727310fe7faf7f1b8f6de Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 9 Nov 2025 00:39:38 -0500 Subject: [PATCH 65/83] Update python library with new interrupt rate method --- bindings/python/CMakeLists.txt | 13 +++++++++---- bindings/python/tslitex.pxd | 2 ++ bindings/python/tslitex.pyx | 13 ++++++++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index dd03a08..0a4959e 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.22...4.1) find_package( Python @@ -19,6 +19,9 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h COMMAND ${CMAKE_COMMAND} -E copy_directory "$" + "$" + "$" + "$" "${CMAKE_CURRENT_BINARY_DIR}/lib" DEPENDS tslitex_static ) @@ -29,9 +32,11 @@ add_custom_target(pydeps tslitex_static ) -set(PY_BIND_LIBS tslitex_static litepcie) +set(PY_BIND_LIBS tslitex_static) + if(WIN32) list(APPEND PY_BIND_LIBS setupapi) + list(APPEND PY_BIND_LIBS litepcie json-c-static zlibstatic) endif() list(JOIN PY_BIND_LIBS "\", \"" PY_EXT_LIBS) @@ -47,7 +52,7 @@ add_custom_target(PyBindings COMMAND Python::Interpreter -m pipx run build --wheel . WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Building python whl" - DEPENDS pydeps tslitex_static + DEPENDS pydeps ) if (NOT DEFINED PY_PLATFORM) @@ -61,7 +66,7 @@ add_custom_target(PyWheels COMMAND Python::Interpreter -m cibuildwheel --platform ${PY_PLATFORM} --archs ${PY_ARCH} --config-file pyproject.toml WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMENT "Building all Python wheels" - DEPENDS pydeps tslitex_static + DEPENDS pydeps ) else() message(STATUS "Python bindings not available: Python not found") diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 27202df..e8f39cc 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -97,6 +97,8 @@ cdef extern from "thunderscope.h": int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolution) + int32_t thunderscopeSampleInterruptRate(tsHandle_t ts, uint32_t interrupt_rate) + int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable) int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) nogil diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index bff09cb..887196c 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -253,11 +253,22 @@ cdef class Thunderscope: @SampleResolution.setter def SampleResolution(self, resolution: int): - assert resolution is 256, f"Support for 8-bit mode only" + assert resolution in [256, 4096], f"Unsupported Resolution" self._sample_resolution = resolution retval = tslitex.thunderscopeSampleModeSet(self._tsHandle, self._sample_rate, self._sample_resolution) + @property + def UpdateRate(self): + return self._interrupt_rate + + @UpdateRate.setter + def UpdateRate(self, rate : int): + assert rate > 0, f"Invalid Update Rate" + self._interrupt_rate = rate + retval = tslitex.thunderscopeSampleInterruptRate(self._tsHandle, + rate) + def Enable(self, enable: bool): cdef int32_t retVal = tslitex.thunderscopeDataEnable(self._tsHandle, enable) if retVal != tslitex.TS_STATUS_OK: From fdf8e003a1d3b9322ebbe9a7bdf92a54e78d43bc Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 15 Nov 2025 09:59:12 -0500 Subject: [PATCH 66/83] Allow multiple TS devices on macOS --- litepcie/CMakeLists.txt | 1 + litepcie/include/litepcie_helpers.h | 5 ++- litepcie/src/litepcie_helpers.c | 56 ++++++++++++++++------------- src/samples.c | 6 ++-- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/litepcie/CMakeLists.txt b/litepcie/CMakeLists.txt index cab4efc..3ebb6f9 100644 --- a/litepcie/CMakeLists.txt +++ b/litepcie/CMakeLists.txt @@ -51,4 +51,5 @@ if(WIN32) target_link_libraries(litepcie setupapi) elseif(APPLE) target_link_libraries(litepcie "-framework IOKit") + target_link_libraries(litepcie "-framework CoreFoundation") endif() \ No newline at end of file diff --git a/litepcie/include/litepcie_helpers.h b/litepcie/include/litepcie_helpers.h index 8b2b0d5..f379438 100644 --- a/litepcie/include/litepcie_helpers.h +++ b/litepcie/include/litepcie_helpers.h @@ -37,9 +37,8 @@ typedef io_connect_t file_t; #define checked_ioctl(...) _check_ioctl((int)IOConnectCallStructMethod(__VA_ARGS__), __FILE__, __LINE__) void _check_ioctl(int status, const char* file, int line); -//TODO: Add support for multiple TS -#define LITEPCIE_CTRL_NAME(ID) "litepcie" -#define LITEPCIE_DMA_NAME(ID, CHAN) "litepcie" +#define LITEPCIE_CTRL_NAME(ID) "thunderscope" #ID +#define LITEPCIE_DMA_NAME(ID, CHAN) "thunderscope" #ID #else #include typedef int file_t; diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 8f8dcc0..0d65d60 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -14,6 +14,7 @@ #include #include #elif defined(__APPLE__) +#include #include #include #include @@ -183,40 +184,45 @@ file_t litepcie_open(const char* name, int32_t flags) fd = CreateFile(devName, (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, flags, NULL); #elif defined(__APPLE__) + uint32_t idx; kern_return_t ret = kIOReturnSuccess; io_iterator_t iterator = IO_OBJECT_NULL; io_service_t service = IO_OBJECT_NULL; io_connect_t connection = IO_OBJECT_NULL; + CFStringRef matchServ = CFSTR("IOUserServerName"); + CFStringRef matchVal = CFSTR("eevengers.thunderscope"); fd = IO_OBJECT_NULL; - /// - Tag: ClientApp_Connect - ret = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceNameMatching(name), &iterator); - if (ret != kIOReturnSuccess) { - printf("Unable to find service for identifier with error: 0x%08x.\n", ret); - _print_kerr_details(ret); - } - else + if(1 == sscanf(name, LITEPCIE_CTRL_NAME(%u), &idx)) { - printf("Searching for dext service...\n"); - while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { - // Open a connection to this user client as a server to that client, and store the instance in "service" - ret = IOServiceOpen(service, mach_task_self_, kIOHIDServerConnectType, &connection); - - if (ret == kIOReturnSuccess) { - printf("\tOpened service.\n"); - fd = connection; - break; - } else { - printf("\tFailed opening service with error: 0x%08x.\n", ret); + CFMutableDictionaryRef matchingDict = IOServiceNameMatching("litepcie"); + CFDictionaryAddValue(matchingDict, matchServ, matchVal); + ret = IOServiceGetMatchingServices(kIOMainPortDefault, matchingDict, &iterator); + if (ret != kIOReturnSuccess) { + fprintf(stderr, "Unable to find service for identifier with error: 0x%08x.\n", ret); + _print_kerr_details(ret); + } + else + { + while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { + if(idx-- > 0) + { + continue; + } + // Open a connection to this user client as a server to that client, and store the instance in "service" + ret = IOServiceOpen(service, mach_task_self_, kIOHIDServerConnectType, &connection); + + if (ret == kIOReturnSuccess) { + fd = connection; + break; + } else { + fprintf(stderr, "\tFailed opening service with error: 0x%08x.\n", ret); + } + + IOObjectRelease(service); } - - IOObjectRelease(service); } - } - IOObjectRelease(iterator); - - if (service == IO_OBJECT_NULL) { - printf("Failed to match to device.\n"); + IOObjectRelease(iterator); } #else fd = open(name, flags); diff --git a/src/samples.c b/src/samples.c index a6a6aaf..fa59f94 100644 --- a/src/samples.c +++ b/src/samples.c @@ -37,9 +37,9 @@ #define TS_DMA_OS_FLAGS (FILE_ATTRIBUTE_NORMAL | \ FILE_FLAG_NO_BUFFERING) #elif defined(__APPLE__) -#define TS_DMA_NAME "%s" -#define TS_DMA_NAME_LEN (16) -#define TS_DMA_NAME_ARGS(chan, dev) "litepcie" +#define TS_DMA_NAME "thunderscope%u" +#define TS_DMA_NAME_LEN (24) +#define TS_DMA_NAME_ARGS(chan, dev) (dev) #define TS_DMA_OS_FLAGS (O_CLOEXEC) #define APPLE_MMAP_DMA #elif defined(__linux__) From 1af5126cd1ecbbb6ecbc7046a506bb037f7a163e Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 28 Nov 2025 12:11:57 -0500 Subject: [PATCH 67/83] Add api to set Ref Clock IN/OUT --- include/thunderscope.h | 10 ++++ include/ts_common.h | 10 ++++ src/mcp_zl3026x.h | 4 ++ src/platform.h | 22 ++++----- src/thunderscope.c | 12 +++++ src/ts_channel.c | 110 +++++++++++++++++++++++++++++++++++++++-- src/ts_channel.h | 10 ++++ 7 files changed, 164 insertions(+), 14 deletions(-) diff --git a/include/thunderscope.h b/include/thunderscope.h index 896253c..2296a06 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -67,6 +67,16 @@ int32_t thunderscopeChannelConfigGet(tsHandle_t ts, uint32_t channel, tsChannelP */ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelParam_t* conf); +/** + * @brief Set the mode and frequency for the external Reference Clock + * + * @param ts Handle to the Thunderscope device + * @param mode Set the Clock IN/OUT mode + * @param refclk_freq Set the input clock frequency if in IN mode, or output frequency if in OUT mode + * @return int32_t TS_STATUS_OK if the reference clock was configured + */ +int32_t thunderscopeRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq); + /** * @brief Get the status for the Thunderscope device * diff --git a/include/ts_common.h b/include/ts_common.h index 5a77536..cc193c4 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -34,11 +34,14 @@ extern "C" { #define TS_HW_ID_VARIANT_MASK (1 << 8) #define TS_HW_ID_VALID_MASK (1 << 9) +#define TS_DEFUALT_CLKOUT_FREQ (10000000) //10 MHz + #define TS_GW_VERSION(major, minor, patch) ((((major) & 0xFFFF) << 16) + \ (((minor) & 0xFF) << 8) + \ (((patch) & 0x3F) << 1)) #define LITEX_VERSION(major, minor) ((((major) & 0xFFFF) << 16) + ((minor) & 0xFFFF)) + /** * @brief Opaque Handle to a Thunderscope device instance * @@ -58,6 +61,13 @@ typedef enum tsChannelTerm_e TS_TERM_50 = 1, } tsChannelTerm_t; +typedef enum tsRefClockMode_e +{ + TS_REFCLK_NONE = 0, + TS_REFCLK_OUT = 1, + TS_REFCLK_IN = 2 +} tsRefClockMode_t; + typedef struct tsDeviceInfo_s { uint32_t device_id; diff --git a/src/mcp_zl3026x.h b/src/mcp_zl3026x.h index 7111f6a..4a6c716 100644 --- a/src/mcp_zl3026x.h +++ b/src/mcp_zl3026x.h @@ -27,6 +27,10 @@ extern "C" { #define ZL3026X_OUT_MDIV_MIN_CLK (375000000) +#define ZL3026X_INPUT_CLK_MIN (9720000) +#define ZL3026X_INPUT_CLK_MAX (156250000) + + typedef enum zl3026x_input_e { ZL3026X_INPUT_IC1, ZL3026X_INPUT_IC2, diff --git a/src/platform.h b/src/platform.h index 3e2201d..470fb80 100644 --- a/src/platform.h +++ b/src/platform.h @@ -99,17 +99,17 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_PLL_CONF ZL30250_CONF #define TS_PLL_CONF_SIZE ZL30250_CONF_SIZE #else -#define TS_PLL_BUS_BETA CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR -#define TS_PLL_BUS_DEV CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR -#define TS_PLL_I2C_ADDR ZL30260_I2C_ADDR -#define TS_PLL_CONF ZL30260_CONF -#define TS_PLL_CONF_SIZE ZL30260_CONF_SIZE -#define TS_PLL_INPUT_IDX (1) -#define TS_PLL_INPUT_RATE (10000000) -#define TS_PLL_INPUT_SEL (ZL3026X_INPUT_IC2) - -#define TS_PLL_REFIN_IDX (0) -#define TS_PLL_REFIN_SEL (ZL3026X_INPUT_IC1) +#define TS_PLL_BUS_BETA CSR_I2CBUS_I2C0_PHY_SPEED_MODE_ADDR +#define TS_PLL_BUS_DEV CSR_I2CBUS_I2C1_PHY_SPEED_MODE_ADDR +#define TS_PLL_I2C_ADDR ZL30260_I2C_ADDR +#define TS_PLL_CONF ZL30260_CONF +#define TS_PLL_CONF_SIZE ZL30260_CONF_SIZE +#define TS_PLL_LOCAL_OSC_IDX (1) +#define TS_PLL_LOCAL_OSC_RATE (10000000) +#define TS_PLL_LOCAL_OSC_SEL (ZL3026X_INPUT_IC2) + +#define TS_PLL_REFIN_IDX (0) +#define TS_PLL_REFIN_SEL (ZL3026X_INPUT_IC1) #define TS_PLL_REFOUT_CLK_IDX (0) #define TS_PLL_REFOUT_RATE_DEFAULT (10000000) diff --git a/src/thunderscope.c b/src/thunderscope.c index bafd8b3..cb642ca 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -217,6 +217,18 @@ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelP return TS_STATUS_ERROR; } +int32_t thunderscopeChannelRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + + if(pInst && pInst->initialized) + { + return ts_channel_ext_clock_config(pInst->pChannel, mode, refclk_freq); + } + + return TS_STATUS_ERROR; +} + int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* state) { ts_inst_t* pInst = (ts_inst_t*)ts; diff --git a/src/ts_channel.c b/src/ts_channel.c index 426e050..e8608a8 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -187,9 +187,11 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) //Set I2C Clock i2c_rate_set(pChan->pll.clkGen, TS_I2C_CLK_RATE); - pChan->pll.clkConf.in_clks[TS_PLL_INPUT_IDX].enable = 1; - pChan->pll.clkConf.in_clks[TS_PLL_INPUT_IDX].input_freq = TS_PLL_INPUT_RATE; - pChan->pll.clkConf.input_select = TS_PLL_INPUT_SEL; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_freq = TS_PLL_LOCAL_OSC_RATE; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_divider = 1; + pChan->pll.clkConf.input_select = TS_PLL_LOCAL_OSC_SEL; + pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_freq = TS_PLL_REFOUT_RATE_DEFAULT; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_mode = TS_PLL_REFOUT_CLK_MODE; @@ -615,6 +617,108 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin return TS_STATUS_OK; } +int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t mode, uint32_t refclk_freq) +{ + if(tsChannels == NULL) + { + return TS_STATUS_ERROR; + } + ts_channel_t* ts = (ts_channel_t*)tsChannels; + zl3026x_clk_config_t newConf = ts->pll.clkConf; + bool clkout_en = (mode == TS_REFCLK_OUT); + bool clkin_en = (mode == TS_REFCLK_IN); + uint8_t clkin_divider = 1; + uint32_t input_freq = TS_PLL_LOCAL_OSC_RATE; + + //Validate Settings + if (clkout_en && refclk_freq == 0) + { + LOG_ERROR("Invalid Clock Out Frequency, cannot be 0"); + return TS_INVALID_PARAM; + } + else if(clkin_en && refclk_freq < ZL3026X_INPUT_CLK_MIN) + { + LOG_ERROR("Invalid Clock Input Frequency %d Hz. Must be a minimum of %d Hz", refclk_freq, ZL3026X_INPUT_CLK_MIN); + return TS_INVALID_PARAM; + } + + if(clkin_en) + { + //Divide Input Clock Frequency if needed + while((refclk_freq / (1 << clkin_divider)) > ZL3026X_INPUT_CLK_MAX) + { + clkin_divider++; + if(clkin_divider > ZL3026X_IN_DIV_8) + { + LOG_ERROR("Unable to configure external clock input frequency %d Hz", refclk_freq); + return TS_INVALID_PARAM; + } + } + + //Set Input Clock Configuration + newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 0; + newConf.in_clks[TS_PLL_REFIN_IDX].enable = 1; + newConf.in_clks[TS_PLL_REFIN_IDX].input_freq = refclk_freq / (1 << clkin_divider); + newConf.in_clks[TS_PLL_REFIN_IDX].input_divider = (zl3026x_input_div_t)clkin_divider; + newConf.input_select = TS_PLL_REFIN_SEL; + + //Input frequency on bypass path + input_freq = newConf.in_clks[TS_PLL_REFIN_IDX].input_freq; + } + else + { + //Use Internal Reference Clock + newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; + newConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; + newConf.input_select = TS_PLL_LOCAL_OSC_SEL; + + } + + //Set Output Clock Configuration + if(clkout_en) + { + //Validate Output Clock Frequency + if(refclk_freq > ZL3026X_MAX_PLL_OUT) + { + LOG_ERROR("Invalid Clock Output Frequency %d Hz. Must be a maximum of %d Hz", refclk_freq, ZL3026X_MAX_PLL_OUT); + return TS_INVALID_PARAM; + } + + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_freq = refclk_freq; + if(refclk_freq > input_freq) + { + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_pll_select = ZL3026X_PLL_INT_DIV; + } + else + { + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_pll_select = ZL3026X_PLL_BYPASS; + } + } + else + { + //Disable Output Ref Clock + newConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 0; + } + + mcp_clkgen_conf_t clk_regs[MCP_CLKGEN_ARR_MAX_LEN] = {0}; + int32_t clk_len = mcp_zl3026x_build_config(clk_regs, MCP_CLKGEN_ARR_MAX_LEN, newConf); + if(clk_len > 0) + { + if(TS_STATUS_OK != mcp_clkgen_config(ts->pll.clkGen, clk_regs, clk_len)) + { + return TS_STATUS_ERROR; + } + ts->pll.clkConf = newConf; + } + else + { + LOG_ERROR("Failed to generate PLL Configuration: %d", clk_len); + return clk_len; + } + + return TS_STATUS_OK; +} int32_t ts_channel_calibration_set(tsChannelHdl_t tsChannels, uint32_t chanIdx, tsChannelCalibration_t* cal) { diff --git a/src/ts_channel.h b/src/ts_channel.h index 0198e5d..32cc0d0 100644 --- a/src/ts_channel.h +++ b/src/ts_channel.h @@ -86,6 +86,16 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels); */ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution); +/** + * @brief Configure the Clock Generator Reference Clock In/Out + * + * @param tsChannels Thunderscope Channel handle + * @param mode Set the Clock IN/OUT mode + * @param refclk_freq Set the input clock frequency if in IN mode, or output frequency if in OUT mode + * @return int32_t TS_STATUS_OK on success, else TS_STATUS_ERROR + */ +int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t mode, uint32_t refclk_freq); + /** * @brief Set the calibration parameters for a channel * From 655f62918c556ced6698b39e37ea57e1d9245eeb Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 28 Nov 2025 22:33:01 -0500 Subject: [PATCH 68/83] Fix clock dividers and add Ref Clk settings for test app --- example/thunderscope_test.cpp | 30 +++++++++++++++++++++++-- src/mcp_zl3026x.c | 41 ++++++++++++++++++++--------------- src/thunderscope.c | 2 +- src/ts_channel.c | 7 +++--- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index e4f270e..cb2ba53 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -306,7 +306,7 @@ static void test_io(file_t fd, bool isBeta) } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, - uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, bool is12bit) + uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, bool is12bit, bool inRefClk, bool outRefClk, uint32_t refclkFreq) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); @@ -317,6 +317,17 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ uint64_t sampleLen = 0; uint32_t sampleRate = 1000000000; + if(inRefClk) + { + printf("Setting Ref In Clock @ %u Hz\n", refclkFreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_IN, refclkFreq)); + } + else if(outRefClk); + { + printf("Setting Ref Out Clock @ %u Hz\n", refclkFreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_OUT, refclkFreq)); + } + //Setup and Enable Channels tsChannelParam_t chConfig = {0}; uint8_t channel = 0; @@ -615,6 +626,9 @@ int main(int argc, char** argv) uint8_t term = 0; bool bitslip = false; bool mode12bit = false; + bool refInClk = false; + bool refOutClk = false; + uint32_t refclkFreq = 0; struct optparse_long argList[] = { {"dev", 'd', OPTPARSE_REQUIRED}, @@ -622,6 +636,8 @@ int main(int argc, char** argv) {"bw", 'b', OPTPARSE_REQUIRED}, {"voltsuv", 'v', OPTPARSE_REQUIRED}, {"offsetuv", 'o', OPTPARSE_REQUIRED}, + {"refinclk", 'i', OPTPARSE_REQUIRED}, + {"refoutclk",'r', OPTPARSE_REQUIRED}, {"ac", 'a', OPTPARSE_NONE}, {"term", 't', OPTPARSE_NONE}, {"bits", 's', OPTPARSE_NONE}, @@ -659,6 +675,16 @@ int main(int argc, char** argv) offset_uV = strtol(options.optarg, NULL, 0); argCount+=2; break; + case 'i': + refInClk = true; + refclkFreq = strtol(options.optarg, NULL, 0); + argCount+=2; + break; + case 'r': + refOutClk = true; + refclkFreq = strtol(options.optarg, NULL, 0); + argCount+=2; + break; case 'a': ac_couple = 1; argCount++; @@ -788,7 +814,7 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit); + test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit, refInClk, refOutClk, refclkFreq); } // Flash test else if(0 == strcmp(arg, "flash")) diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index 9c9c861..1faa476 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -32,7 +32,7 @@ static uint64_t mcp_zl3026x_selected_input_freq(zl3026x_clk_config_t *conf); int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl3026x_clk_config_t conf) { int32_t calLen = 0; - + uint32_t scaled_in_freq = 0; // Reference App Note ZLAN-590 // https://ww1.microchip.com/downloads/aemDocuments/documents/TCG/ApplicationNotes/ApplicationNotes/ConfigurationSequenceZLAN-590.pdf @@ -85,6 +85,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider); calLen++; in_ch_bitmap |= (1 << ch); + scaled_in_freq = conf.in_clks[ch].input_freq / (1 << conf.in_clks[ch].input_divider); break; } } @@ -197,6 +198,8 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 } } + LOG_DEBUG("Calculated APLL Output Freq: %llu Hz", pll_out); + uint64_t pll_vco = 4200000000; uint32_t pll_int_div = pll_vco/pll_out; //validate pll_int_div 4-15 @@ -369,6 +372,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.out_clks[ch].enable) { + LOG_DEBUG("Setting Output CLK %d to %llu Hz", ch, conf.out_clks[ch].output_freq); /** * The frequency of the clock from the medium-speed divider is divided by LSDIV+1. * @@ -382,32 +386,35 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 uint8_t out_div, out_div_med = 1; out_div = 0x80; //Phase align output - if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) + if(((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) && + (conf.out_clks[ch].output_freq != pll_out)) || + ((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) && + (conf.out_clks[ch].output_freq != scaled_in_freq))) { - if(conf.out_clks[ch].output_freq != pll_out) + out_divide = pll_out / conf.out_clks[ch].output_freq; + // Note: output_freq = pll_out / (div_med * div_low) + while(out_divide >= (1 << 7)) { - out_divide = pll_out / conf.out_clks[ch].output_freq; - // Note: output_freq = pll_out / (div_med * div_low) - while(out_divide >= (1 << 7)) + if(out_divide & 0x1) { - if(out_divide & 0x1) - { - LOG_ERROR("======== TODO: FIX OUTPUT DIVIDE ======="); - return TS_STATUS_ERROR; - } - out_divide >>= 1; - out_div_low <<= 1; + LOG_ERROR("======== TODO: FIX OUTPUT DIVIDE ======="); + return TS_STATUS_ERROR; } - - out_div_med = out_divide; - - out_div |= (out_div_med-1) & 0x7F; + out_divide >>= 1; + out_div_low <<= 1; } + + out_div_med = out_divide; + + out_div |= (out_div_med-1) & 0x7F; } + LOG_DEBUG("Setting Output CLK %d LDIV to %02X", ch, out_div_low); + LOG_DEBUG("Setting Output CLK %d CR1 to %02X", ch, out_div); MCP_ADD_REG_WRITE(&confData[calLen], 0x0200+(ch*0x10), out_div); calLen++; + LOG_DEBUG("Setting Output CLK %d mode to %02X", ch, conf.out_clks[ch].output_mode); MCP_ADD_REG_WRITE(&confData[calLen], 0x0201+(ch*0x10), conf.out_clks[ch].output_mode); calLen++; diff --git a/src/thunderscope.c b/src/thunderscope.c index cb642ca..1471c42 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -217,7 +217,7 @@ int32_t thunderscopeChannelConfigSet(tsHandle_t ts, uint32_t channel, tsChannelP return TS_STATUS_ERROR; } -int32_t thunderscopeChannelRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq) +int32_t thunderscopeRefClockSet(tsHandle_t ts, tsRefClockMode_t mode, uint32_t refclk_freq) { ts_inst_t* pInst = (ts_inst_t*)ts; diff --git a/src/ts_channel.c b/src/ts_channel.c index e8608a8..080bb20 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -189,7 +189,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_freq = TS_PLL_LOCAL_OSC_RATE; - pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_divider = 1; + pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_divider = 0; pChan->pll.clkConf.input_select = TS_PLL_LOCAL_OSC_SEL; pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; @@ -627,7 +627,7 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t zl3026x_clk_config_t newConf = ts->pll.clkConf; bool clkout_en = (mode == TS_REFCLK_OUT); bool clkin_en = (mode == TS_REFCLK_IN); - uint8_t clkin_divider = 1; + uint8_t clkin_divider = 0; uint32_t input_freq = TS_PLL_LOCAL_OSC_RATE; //Validate Settings @@ -645,7 +645,7 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t if(clkin_en) { //Divide Input Clock Frequency if needed - while((refclk_freq / (1 << clkin_divider)) > ZL3026X_INPUT_CLK_MAX) + while((refclk_freq / (1UL << clkin_divider)) > ZL3026X_INPUT_CLK_MAX) { clkin_divider++; if(clkin_divider > ZL3026X_IN_DIV_8) @@ -671,7 +671,6 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; newConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; newConf.input_select = TS_PLL_LOCAL_OSC_SEL; - } //Set Output Clock Configuration From 6fa70afe85011b22f9246038a2a8c27a3f22f226 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 28 Nov 2025 23:00:13 -0500 Subject: [PATCH 69/83] Divide output clock from the correct source frequency --- src/mcp_zl3026x.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index 1faa476..ab020d9 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -386,13 +386,20 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 uint8_t out_div, out_div_med = 1; out_div = 0x80; //Phase align output - if(((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) && - (conf.out_clks[ch].output_freq != pll_out)) || - ((conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) && - (conf.out_clks[ch].output_freq != scaled_in_freq))) + uint32_t clksrc_freq = 0; + if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_INT_DIV) { - out_divide = pll_out / conf.out_clks[ch].output_freq; - // Note: output_freq = pll_out / (div_med * div_low) + clksrc_freq = pll_out; + } + else if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) + { + clksrc_freq = scaled_in_freq; + } + + if(conf.out_clks[ch].output_freq != clksrc_freq) + { + out_divide = clksrc_freq / conf.out_clks[ch].output_freq; + // Note: output_freq = clksrc_freq / (div_med * div_low) while(out_divide >= (1 << 7)) { if(out_divide & 0x1) From 5cb78422b107f7287395fe1e141f7f77f7d252cd Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 29 Nov 2025 12:06:22 -0500 Subject: [PATCH 70/83] Fix typo in test app --- CMakeLists.txt | 2 -- example/thunderscope_test.cpp | 2 +- src/mcp_zl3026x.c | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 89a8d84..d07df18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,6 @@ FetchContent_Declare( json_c GIT_REPOSITORY https://github.com/json-c/json-c GIT_TAG 2372e9518e6ba95b48d37ec162bc7d93b297b52f - FIND_PACKAGE_ARGS NAMES json-c ) set(BUILD_SHARED_LIBS OFF) @@ -40,7 +39,6 @@ FetchContent_Declare( zlib GIT_REPOSITORY https://github.com/madler/zlib.git GIT_TAG v1.3.1 - FIND_PACKAGE_ARGS NAMES zlib ) set(ZLIB_BUILD_TESTING OFF) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index cb2ba53..f305434 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -322,7 +322,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ printf("Setting Ref In Clock @ %u Hz\n", refclkFreq); printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_IN, refclkFreq)); } - else if(outRefClk); + else if(outRefClk) { printf("Setting Ref Out Clock @ %u Hz\n", refclkFreq); printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_OUT, refclkFreq)); diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index ab020d9..ef6bc39 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -82,6 +82,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.in_clks[ch].enable) { + LOG_DEBUG("Input Clock %d: Freq %u Hz, Div %d", ch, conf.in_clks[ch].input_freq, conf.in_clks[ch].input_divider); MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider); calLen++; in_ch_bitmap |= (1 << ch); From 9980b7e4cfb952699bd270a74a83cd49263cc212 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 29 Nov 2025 17:41:16 -0500 Subject: [PATCH 71/83] Add alternate input clock configuration and PLL clock status flags --- bindings/python/tslitex.pxd | 6 ++++++ example/tsControl.py | 3 ++- include/ts_common.h | 6 ++++++ src/mcp_clkgen.c | 29 +++++++++++++++++++++++++++++ src/mcp_clkgen.h | 14 ++++++++++++++ src/mcp_zl3026x.c | 9 +++++++-- src/mcp_zl3026x.h | 6 +++++- src/platform.c | 13 +++++++++++++ src/platform.h | 16 ++++++++++++++++ src/ts_channel.c | 22 +++++++++++++++++++--- 10 files changed, 117 insertions(+), 7 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index e8f39cc..8776bd0 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -79,6 +79,12 @@ cdef extern from "thunderscope.h": uint8_t power_state uint8_t pll_state uint8_t afe_state + uint8_t local_osc_clk + uint8_t ref_in_clk + uint8_t pll_lock + uint8_t pll_low + uint8_t pll_high + uint8_t pll_alt sysHealth_t sys_health ctypedef tsScopeState_s tsScopeState_t diff --git a/example/tsControl.py b/example/tsControl.py index f8687b2..a427691 100755 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -264,7 +264,8 @@ def create_status_tab(notebook, delegate): "adc_sample_rate", "adc_sample_bits", "adc_sample_resolution", "adc_lost_buffer_count", "flags", "adc_state", "power_state", "pll_state", "afe_state", "sys_health.temp_c", "sys_health.vcc_int", "sys_health.vcc_aux", - "sys_health.vcc_bram", "sys_health.frontend_power_good", "sys_health.acq_power_good" + "sys_health.vcc_bram", "sys_health.frontend_power_good", "sys_health.acq_power_good", + "local_osc_clk", "ref_in_clk", "pll_lock", "pll_low", "pll_high", "pll_alt" ] # Create labels and fields for each key diff --git a/include/ts_common.h b/include/ts_common.h index cc193c4..d945310 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -119,6 +119,12 @@ typedef struct tsScopeState_s uint8_t power_state:1; uint8_t pll_state:1; uint8_t afe_state:1; + uint8_t local_osc_clk:1; + uint8_t ref_in_clk:1; + uint8_t pll_lock:1; + uint8_t pll_low:1; + uint8_t pll_high:1; + uint8_t pll_alt:1; }; }; sysHealth_t sys_health; diff --git a/src/mcp_clkgen.c b/src/mcp_clkgen.c index 637bee8..f9a34ca 100644 --- a/src/mcp_clkgen.c +++ b/src/mcp_clkgen.c @@ -59,6 +59,35 @@ int32_t mcp_clkgen_config(i2c_t device, const mcp_clkgen_conf_t* confData, uint3 return TS_STATUS_OK; } +int32_t mcp_clkgen_status(i2c_t device, const mcp_clkgen_status_t* statusData, uint32_t statusLen) +{ + int32_t status = 0; + + if(NULL == statusData) + { + //Error + return TS_STATUS_ERROR; + } + LOG_DEBUG("Status Regs:"); + + for(uint32_t i = 0; i < statusLen; i++) + { + uint8_t data[1] = {0}; + if(!i2c_read(device, (uint32_t)ZL302XX_READ_REG(statusData[i].addr), + data, 1, ZL302XX_ADDR_LEN)) + { + LOG_DEBUG("\t%06X (NACK!)", (uint32_t)ZL302XX_READ_REG(statusData[i].addr)); + return TS_STATUS_ERROR; + } + LOG_DEBUG("\t%06X : %02X", (uint32_t)ZL302XX_WRITE_REG(statusData[i].addr), data[0]); + + status |= (data[0] & statusData[i].mask) ? (1 << i) : 0; + } + + return status; +} + + void mcp_clkgen_regdump(i2c_t device, const mcp_clkgen_conf_t* confData, uint32_t confLen) { diff --git a/src/mcp_clkgen.h b/src/mcp_clkgen.h index 396631b..9baca15 100644 --- a/src/mcp_clkgen.h +++ b/src/mcp_clkgen.h @@ -33,6 +33,11 @@ typedef struct mcp_clkgen_conf_s { }; }mcp_clkgen_conf_t; +typedef struct mcp_clkgen_status_s { + uint16_t addr; + uint8_t mask; +} mcp_clkgen_status_t; + /** * @brief Configure the PLL with a list of registers. * @@ -43,6 +48,15 @@ typedef struct mcp_clkgen_conf_s { */ int32_t mcp_clkgen_config(i2c_t device, const mcp_clkgen_conf_t* confData, uint32_t confLen); +/** + * @brief Get a set of status flags for the PLL + * @param device I2C device for the zl30260/zl30250 + * @param statusData Array of status registers to read + * @param statusLen Length of the status array + * @return int32_t Bitfield of status flags, or TS_STATUS_ERROR on error + */ +int32_t mcp_clkgen_status(i2c_t device, const mcp_clkgen_status_t* statusData, uint32_t statusLen); + /** * @brief Debug function to print all register values and compare them to written values * diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index ef6bc39..f5560c9 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -83,7 +83,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 if(conf.in_clks[ch].enable) { LOG_DEBUG("Input Clock %d: Freq %u Hz, Div %d", ch, conf.in_clks[ch].input_freq, conf.in_clks[ch].input_divider); - MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider); + MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider | ZL3026X_VALTIME_DEFAULT); calLen++; in_ch_bitmap |= (1 << ch); scaled_in_freq = conf.in_clks[ch].input_freq / (1 << conf.in_clks[ch].input_divider); @@ -240,7 +240,12 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 MCP_ADD_REG_WRITE(&confData[calLen], 0x0101, pll_int_div & 0x0F); //PLL Output Integer Divide calLen++; - MCP_ADD_REG_WRITE(&confData[calLen], 0x0102, (conf.input_select & 0x7)); //PLL Input + uint8_t apll_cr3 = (conf.input_select & 0x7); //PLL Input + if(conf.alternate_select != ZL3026X_INPUT_NONE) + { + apll_cr3 |= 0x80 | ((conf.alternate_select & 0x07) << 3); //Enable Input Monitoring and Alternate PLL Input + } + MCP_ADD_REG_WRITE(&confData[calLen], 0x0102, apll_cr3); //PLL Input calLen++; //PLL AFBDIV diff --git a/src/mcp_zl3026x.h b/src/mcp_zl3026x.h index 4a6c716..8bd40ca 100644 --- a/src/mcp_zl3026x.h +++ b/src/mcp_zl3026x.h @@ -30,13 +30,16 @@ extern "C" { #define ZL3026X_INPUT_CLK_MIN (9720000) #define ZL3026X_INPUT_CLK_MAX (156250000) +#define ZL3026X_VALTIME_DEFAULT (0x0C) + typedef enum zl3026x_input_e { ZL3026X_INPUT_IC1, ZL3026X_INPUT_IC2, ZL3026X_INPUT_IC3, ZL3026X_INPUT_XO, - ZL3026X_INPUT_XO_DBL + ZL3026X_INPUT_XO_DBL, + ZL3026X_INPUT_NONE } zl3026x_input_t; typedef enum zl3026x_input_div_e { @@ -89,6 +92,7 @@ typedef struct zl3026x_clk_config_s { zl3026x_input_clk_t in_clks[ZL3026X_NUM_INPUT_CLK]; zl3026x_output_clk_t out_clks[ZL3026X_NUM_OUTPUT_CLK]; zl3026x_input_t input_select; + zl3026x_input_t alternate_select; //TODO Add GPIO Config } zl3026x_clk_config_t; diff --git a/src/platform.c b/src/platform.c index 9f6ec88..53fab5c 100644 --- a/src/platform.c +++ b/src/platform.c @@ -52,6 +52,19 @@ const mcp_clkgen_conf_t ZL30260_CONF[] = { const uint32_t ZL30260_CONF_SIZE = sizeof(ZL30260_CONF)/sizeof(ZL30260_CONF[0]); +const mcp_clkgen_status_t ZL30260_STATUS[] = { + {.addr=0x004C, .mask=0x02}, //XA.XAV Status + {.addr=0x004D, .mask=0x02}, //IC1.ICV Status + {.addr=0x004E, .mask=0x02}, //IC2.ICV Status + {.addr=0x004F, .mask=0x02}, //IC3.ICV Status + {.addr=0x0048, .mask=0x04}, //APLL.ALK Status + {.addr=0x0048, .mask=0x40}, //APLL.AIFL Status + {.addr=0x0048, .mask=0x10}, //APLL.AIFH Status + {.addr=0x0048, .mask=0x01}, //APLL.SELREF Status +}; + +const uint32_t ZL30260_STATUS_SIZE = sizeof(ZL30260_STATUS)/sizeof(ZL30260_STATUS[0]); + const mcp_clkgen_conf_t ZL30250_CONF[] = { {.action=MCP_CLKGEN_WRITE_REG, .addr=0x0009, .value=0x02}, {.action=MCP_CLKGEN_WRITE_REG, .addr=0x0621, .value=0x08}, diff --git a/src/platform.h b/src/platform.h index 470fb80..c97b290 100644 --- a/src/platform.h +++ b/src/platform.h @@ -94,6 +94,9 @@ extern const uint32_t ZL30250_CONF_SIZE; extern const mcp_clkgen_conf_t ZL30260_CONF[]; extern const uint32_t ZL30260_CONF_SIZE; +extern const mcp_clkgen_status_t ZL30260_STATUS[]; +extern const uint32_t ZL30260_STATUS_SIZE; + #ifdef TS_REV_3 #define TS_PLL_I2C_ADDR ZL30250_I2C_ADDR #define TS_PLL_CONF ZL30250_CONF @@ -104,6 +107,8 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_PLL_I2C_ADDR ZL30260_I2C_ADDR #define TS_PLL_CONF ZL30260_CONF #define TS_PLL_CONF_SIZE ZL30260_CONF_SIZE +#define TS_PLL_STATUS ZL30260_STATUS +#define TS_PLL_STATUS_LEN ZL30260_STATUS_SIZE #define TS_PLL_LOCAL_OSC_IDX (1) #define TS_PLL_LOCAL_OSC_RATE (10000000) #define TS_PLL_LOCAL_OSC_SEL (ZL3026X_INPUT_IC2) @@ -111,6 +116,8 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_PLL_REFIN_IDX (0) #define TS_PLL_REFIN_SEL (ZL3026X_INPUT_IC1) +#define TS_PLL_INPUT_NONE_SEL (ZL3026X_INPUT_NONE) + #define TS_PLL_REFOUT_CLK_IDX (0) #define TS_PLL_REFOUT_RATE_DEFAULT (10000000) #define TS_PLL_REFOUT_CLK_MODE (ZL3026X_OUT_CMOS_P) @@ -120,6 +127,15 @@ extern const uint32_t ZL30260_CONF_SIZE; #define TS_PLL_SAMPLE_RATE_DEFAULT (1000000000) #define TS_PLL_SAMPLE_CLK_MODE (ZL3026X_OUT_DIFF) #define TS_PLL_SAMPLE_PLL_MODE (ZL3026X_PLL_INT_DIV) + +#define TS_PLL_STATUS_XA_VALID (0) +#define TS_PLL_STATUS_IC1_VALID (1) +#define TS_PLL_STATUS_IC2_VALID (2) +#define TS_PLL_STATUS_IC3_VALID (3) +#define TS_PLL_STATUS_APLL_LOCK (4) +#define TS_PLL_STATUS_APLL_LOW (5) +#define TS_PLL_STATUS_APLL_HIGH (6) +#define TS_PLL_STATUS_APLL_ALT (7) #endif #define TS_PLL_NRST_ADDR CSR_ADC_CONTROL_ADDR diff --git a/src/ts_channel.c b/src/ts_channel.c index 080bb20..32f48bf 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -191,6 +191,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_freq = TS_PLL_LOCAL_OSC_RATE; pChan->pll.clkConf.in_clks[TS_PLL_LOCAL_OSC_IDX].input_divider = 0; pChan->pll.clkConf.input_select = TS_PLL_LOCAL_OSC_SEL; + pChan->pll.clkConf.alternate_select = TS_PLL_INPUT_NONE_SEL; pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_freq = TS_PLL_REFOUT_RATE_DEFAULT; @@ -530,7 +531,22 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels) //Update XADC values ts_channel_health_update(pTsHdl); - return ((ts_channel_t*)tsChannels)->status; + //Update Clock Status + int32_t clock_status = mcp_clkgen_status(pTsHdl->pll.clkGen, TS_PLL_STATUS, TS_PLL_STATUS_LEN); + if(clock_status < 0) + { + LOG_ERROR("Failed to read PLL Clock Status %d", clock_status); + } + else + { + pTsHdl->status.local_osc_clk = (clock_status & (1 << TS_PLL_LOCAL_OSC_IDX)) ? 1:0; + pTsHdl->status.ref_in_clk = (clock_status & (1 << TS_PLL_REFIN_IDX)) ? 1:0; + pTsHdl->status.pll_lock = (clock_status & (1 << TS_PLL_STATUS_APLL_LOCK)) ? 1:0; + pTsHdl->status.pll_low = (clock_status & (1 << TS_PLL_STATUS_APLL_LOW)) ? 1:0; + pTsHdl->status.pll_high = (clock_status & (1 << TS_PLL_STATUS_APLL_HIGH)) ? 1:0; + pTsHdl->status.pll_alt = (clock_status & (1 << TS_PLL_STATUS_APLL_ALT)) ? 1:0; + } + return pTsHdl->status; } int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution) @@ -656,11 +672,11 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t } //Set Input Clock Configuration - newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 0; newConf.in_clks[TS_PLL_REFIN_IDX].enable = 1; newConf.in_clks[TS_PLL_REFIN_IDX].input_freq = refclk_freq / (1 << clkin_divider); newConf.in_clks[TS_PLL_REFIN_IDX].input_divider = (zl3026x_input_div_t)clkin_divider; newConf.input_select = TS_PLL_REFIN_SEL; + newConf.alternate_select = TS_PLL_LOCAL_OSC_SEL; //Input frequency on bypass path input_freq = newConf.in_clks[TS_PLL_REFIN_IDX].input_freq; @@ -668,9 +684,9 @@ int32_t ts_channel_ext_clock_config(tsChannelHdl_t tsChannels, tsRefClockMode_t else { //Use Internal Reference Clock - newConf.in_clks[TS_PLL_LOCAL_OSC_IDX].enable = 1; newConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; newConf.input_select = TS_PLL_LOCAL_OSC_SEL; + newConf.alternate_select = TS_PLL_INPUT_NONE_SEL; } //Set Output Clock Configuration From e9bdea97d82aee9e379c97f5ccea573d4b084238 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sat, 29 Nov 2025 18:43:30 -0500 Subject: [PATCH 72/83] Use right status bits for clock inputs --- src/ts_channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ts_channel.c b/src/ts_channel.c index 32f48bf..99f105a 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -539,8 +539,8 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels) } else { - pTsHdl->status.local_osc_clk = (clock_status & (1 << TS_PLL_LOCAL_OSC_IDX)) ? 1:0; - pTsHdl->status.ref_in_clk = (clock_status & (1 << TS_PLL_REFIN_IDX)) ? 1:0; + pTsHdl->status.local_osc_clk = (clock_status & (1 << TS_PLL_STATUS_IC2_VALID)) ? 1:0; + pTsHdl->status.ref_in_clk = (clock_status & (1 << TS_PLL_STATUS_IC1_VALID)) ? 1:0; pTsHdl->status.pll_lock = (clock_status & (1 << TS_PLL_STATUS_APLL_LOCK)) ? 1:0; pTsHdl->status.pll_low = (clock_status & (1 << TS_PLL_STATUS_APLL_LOW)) ? 1:0; pTsHdl->status.pll_high = (clock_status & (1 << TS_PLL_STATUS_APLL_HIGH)) ? 1:0; From 7a8e591372f617ddb427fe65b68ec5839acbba94 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 30 Nov 2025 02:33:53 -0500 Subject: [PATCH 73/83] Fix multiple clocks. Add clock test function. --- example/thunderscope_test.cpp | 111 +++++++++++++++++++++++++++++++++- example/tsControl.py | 2 +- src/mcp_zl3026x.c | 7 +-- src/ts_channel.c | 1 + 4 files changed, 114 insertions(+), 7 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index f305434..19ea970 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -27,6 +27,8 @@ #ifdef _WIN32 #include +#else +#include #endif #define OPTPARSE_IMPLEMENTATION @@ -95,10 +97,28 @@ uint32_t _SPI_CONTROL_START = (1 << 0); uint32_t _SPI_CONTROL_LENGTH = (1 << 8); uint32_t _SPI_STATUS_DONE = (1 << 0); +static volatile bool g_program_loop = true; /* Main */ /*------*/ +#ifdef _WIN32 +BOOL WINAPI SigHandler(DWORD ctrlType) +{ + if(ctrlType == CTRL_C_EVENT) + { + g_program_loop = false; + return TRUE; + } + return FALSE; +} +#else +extern "C" void SigHandler(int s) +{ + g_program_loop = false; +} +#endif + void configure_frontend_ldo(file_t fd, uint32_t enable) { uint32_t control_value = litepcie_readl(fd, CSR_FRONTEND_CONTROL_ADDR); control_value &= ~(1 * AFE_CONTROL_LDO_EN); @@ -389,7 +409,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ auto startTime = std::chrono::steady_clock::now(); if(sampleBuffer != NULL) { - for(uint32_t loop=0; loop < 8; loop++) + for(uint32_t loop=0; loop < 150; loop++) { uint32_t readReq = (TS_SAMPLE_BUFFER_SIZE * 0x100); //Collect Samples @@ -407,12 +427,15 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ if(watch_bitslip) { + tsScopeState_t scopeState = {0}; bitslip_count = litepcie_readl(fd, CSR_ADC_HMCAD1520_BITSLIP_COUNT_ADDR); printf("Bitslip Snapshot: %lu\r\n", bitslip_count); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_FRAME_DEBUG_ADDR); printf("FRAME Debug: 0x%08x\r\n", dbg_monitor); dbg_monitor = litepcie_readl(fd, CSR_ADC_HMCAD1520_RANGE_ADDR); printf("RANGE: 0x%08x\r\n", dbg_monitor); + thunderscopeStatusGet(tsHdl, &scopeState); + printf("Scope State Flags: 0x%08x\r\n", scopeState.flags); } } } @@ -587,6 +610,61 @@ static void flash_test(char* arg, file_t fd) } } +static void test_clock(uint32_t idx, bool refoutclk, bool refinclk, uint32_t refclkfreq) +{ + tsHandle_t tsHdl = thunderscopeOpen(idx, false); + bool firstStatus = false; + + if(tsHdl) + { +#ifdef _WIN32 + SetConsoleCtrlHandler(SigHandler, TRUE); +#else + struct sigaction signalHandler; + signalHandler.sa_handler = SigHandler; + sigemptyset(&signalHandler.sa_mask); + signalHandler.sa_flags = 0; + sigaction(SIGINT, &signalHandler, NULL); +#endif + if(refinclk) + { + printf("Setting Ref In Clock @ %u Hz\n", refclkfreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_IN, refclkfreq)); + } + else if(refoutclk) + { + printf("Setting Ref Out Clock @ %u Hz\n", refclkfreq); + printf("\t Result: %i\n", thunderscopeRefClockSet(tsHdl, TS_REFCLK_OUT, refclkfreq)); + } + + while (g_program_loop) + { + tsScopeState_t state = {0}; + thunderscopeStatusGet(tsHdl, &state); + // if(!firstStatus) + // { + // printf("\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A"); + // firstStatus = true; + // } + printf("-------\r\n"); + printf("PLL Status:\r\n"); + printf("\tPLL LOCK - %01x\r\n", state.pll_lock); + printf("\tPLL HIGH - %01x\r\n", state.pll_high); + printf("\tPLL LOW - %01x\r\n", state.pll_low); + printf("\tPLL ALT - %01x\r\n", state.pll_alt); + printf("INPUT Clock Status:\r\n"); + printf("\tLocal Valid - %01x\r\n", state.local_osc_clk); + printf("\tREF IN Valid - %01x\r\n", state.ref_in_clk); + printf("\r\n-- PRESS CTRL+C TO STOP --\r\n"); + + std::cout.flush(); + std::this_thread::sleep_until(awake_time()); + } + + thunderscopeClose(tsHdl); + } +} + static void print_help(void) { printf("TS Test Util Usage:\r\n"); @@ -603,6 +681,9 @@ static void print_help(void) printf("\t\t -o Channel Offset [microvolt]\r\n"); printf("\t\t -a AC Couple\r\n"); printf("\t\t -t 50 Ohm termination\r\n"); + printf("\t refclk - run the PLL source with different clock configurations\r\n"); + printf("\t\t -i Set the Ref IN Clock frequency\r\n"); + printf("\t\t -r Set the Ref OUT Clock frequency\r\n"); } /* Main */ @@ -715,6 +796,29 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } + if(0 == strcmp(arg, "list")) + { + uint32_t i = 0; + tsDeviceInfo_t infos; + while(TS_STATUS_OK == thunderscopeListDevices(i, &infos)) + { + if(i==0) + { + printf("Found ThunderScope(s):\n"); + } + printf("\t%3d | Serial Number: %s\n", i, infos.serial_number); + printf("\t | HW Rev: 0x%x\n", infos.hw_id); + printf("\t | GW Rev: 0x%x\n", infos.gw_id); + printf("\t | LiteX Rev: 0x%x\n", infos.litex); + i++; + } + if(i == 0) + { + printf("No devices present\n"); + } + exit(EXIT_SUCCESS); + } + if(0 == strcmp(arg, "clk")) { mcp_clkgen_conf_t test_conf[1024]; @@ -821,6 +925,11 @@ int main(int argc, char** argv) { flash_test(argv[argCount+1], fd); } + // Test REF Clock Modes + else if(0 == strcmp(arg, "clock")) + { + test_clock(idx, refOutClk, refInClk, refclkFreq); + } //Print Help else { diff --git a/example/tsControl.py b/example/tsControl.py index a427691..4eb81cd 100755 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -262,7 +262,7 @@ def create_status_tab(notebook, delegate): # Define the keys from tsScopeState_t keys = [ "adc_sample_rate", "adc_sample_bits", "adc_sample_resolution", "adc_lost_buffer_count", - "flags", "adc_state", "power_state", "pll_state", "afe_state", + "flags", "adc_state", "adc_sync", "power_state", "pll_state", "afe_state", "sys_health.temp_c", "sys_health.vcc_int", "sys_health.vcc_aux", "sys_health.vcc_bram", "sys_health.frontend_power_good", "sys_health.acq_power_good", "local_osc_clk", "ref_in_clk", "pll_lock", "pll_low", "pll_high", "pll_alt" diff --git a/src/mcp_zl3026x.c b/src/mcp_zl3026x.c index f5560c9..c758152 100644 --- a/src/mcp_zl3026x.c +++ b/src/mcp_zl3026x.c @@ -32,7 +32,6 @@ static uint64_t mcp_zl3026x_selected_input_freq(zl3026x_clk_config_t *conf); int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl3026x_clk_config_t conf) { int32_t calLen = 0; - uint32_t scaled_in_freq = 0; // Reference App Note ZLAN-590 // https://ww1.microchip.com/downloads/aemDocuments/documents/TCG/ApplicationNotes/ApplicationNotes/ConfigurationSequenceZLAN-590.pdf @@ -82,12 +81,10 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 { if(conf.in_clks[ch].enable) { - LOG_DEBUG("Input Clock %d: Freq %u Hz, Div %d", ch, conf.in_clks[ch].input_freq, conf.in_clks[ch].input_divider); + LOG_DEBUG("Input Clock %d: Freq %llu Hz, Div %d", ch, conf.in_clks[ch].input_freq, conf.in_clks[ch].input_divider); MCP_ADD_REG_WRITE(&confData[calLen], (0x0303+ch), (uint8_t)conf.in_clks[ch].input_divider | ZL3026X_VALTIME_DEFAULT); calLen++; in_ch_bitmap |= (1 << ch); - scaled_in_freq = conf.in_clks[ch].input_freq / (1 << conf.in_clks[ch].input_divider); - break; } } if(in_ch_bitmap == 0) @@ -399,7 +396,7 @@ int32_t mcp_zl3026x_build_config(mcp_clkgen_conf_t* confData, uint32_t len, zl30 } else if(conf.out_clks[ch].output_pll_select == ZL3026X_PLL_BYPASS) { - clksrc_freq = scaled_in_freq; + clksrc_freq = mcp_zl3026x_selected_input_freq(&conf); } if(conf.out_clks[ch].output_freq != clksrc_freq) diff --git a/src/ts_channel.c b/src/ts_channel.c index 99f105a..6f5029f 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -193,6 +193,7 @@ int32_t ts_channel_init(tsChannelHdl_t* pTsChannels, file_t ts) pChan->pll.clkConf.input_select = TS_PLL_LOCAL_OSC_SEL; pChan->pll.clkConf.alternate_select = TS_PLL_INPUT_NONE_SEL; pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].enable = 0; + pChan->pll.clkConf.in_clks[TS_PLL_REFIN_IDX].input_divider = 0; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].enable = 1; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_freq = TS_PLL_REFOUT_RATE_DEFAULT; pChan->pll.clkConf.out_clks[TS_PLL_REFOUT_CLK_IDX].output_mode = TS_PLL_REFOUT_CLK_MODE; From c173b2cdccad8ee37b7fde39a135b461237aabf6 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Sun, 30 Nov 2025 17:09:20 -0500 Subject: [PATCH 74/83] Update linking for py binding library --- CMakeLists.txt | 31 +++++++++++++++++++++++-------- bindings/python/CMakeLists.txt | 13 ++++++++----- example/tsControl.py | 2 +- litepcie/CMakeLists.txt | 6 +++--- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d07df18..4a69997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,11 +27,11 @@ include(FetchContent) # json-c Library FetchContent_Declare( json_c - GIT_REPOSITORY https://github.com/json-c/json-c + GIT_REPOSITORY https://github.com/json-c/json-c.git GIT_TAG 2372e9518e6ba95b48d37ec162bc7d93b297b52f ) - -set(BUILD_SHARED_LIBS OFF) +set(JSON_C_BUILD_APPS OFF) +set(JSON_C_BUILD_SHARED_LIBS OFF) FetchContent_MakeAvailable(json_c) # zlib Library @@ -129,11 +129,16 @@ set_target_properties(tslitex PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$<0:> ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/$<0:>) -target_link_libraries(tslitex PUBLIC litepcie) -target_link_libraries(tslitex PRIVATE json-c-static zlibstatic) +target_link_libraries(tslitex + PUBLIC + "$" + PRIVATE + json-c-static + zlibstatic +) if(APPLE) - target_link_libraries(tslitex PRIVATE "-framework IOKit") + target_link_libraries(tslitex PRIVATE "$") endif() target_include_directories(tslitex PUBLIC include) @@ -153,10 +158,20 @@ set_target_properties(tslitex_static PROPERTIES set_target_properties(tslitex_static PROPERTIES POSITION_INDEPENDENT_CODE 1) -target_link_libraries(tslitex_static PUBLIC litepcie) -target_link_libraries(tslitex_static PRIVATE json-c-static zlibstatic) +target_link_libraries(tslitex_static + PUBLIC + "$" + PRIVATE + json-c-static + zlibstatic +) + target_include_directories(tslitex_static PUBLIC include) +if(APPLE) + target_link_libraries(tslitex_static PRIVATE "$") +endif() + file(COPY ${TS_LIB_HEADERS} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/include) #### diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 0a4959e..6083914 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -27,16 +27,19 @@ add_custom_command( ) add_custom_target(pydeps - DEPENDS tslitex/tslitex.c - ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h - tslitex_static + DEPENDS + tslitex/tslitex.c + ${CMAKE_CURRENT_BINARY_DIR}/lib/include/thunderscope.h + tslitex_static ) set(PY_BIND_LIBS tslitex_static) if(WIN32) - list(APPEND PY_BIND_LIBS setupapi) - list(APPEND PY_BIND_LIBS litepcie json-c-static zlibstatic) +list(APPEND PY_BIND_LIBS setupapi) +list(APPEND PY_BIND_LIBS litepcie json-c-static zlibstatic) +else() +list(APPEND PY_BIND_LIBS litepcie json-c z) endif() list(JOIN PY_BIND_LIBS "\", \"" PY_EXT_LIBS) diff --git a/example/tsControl.py b/example/tsControl.py index 4eb81cd..11c93fa 100755 --- a/example/tsControl.py +++ b/example/tsControl.py @@ -203,7 +203,7 @@ def reset_fields(self): # Create the main application window root = tk.Tk() root.title("Thunderscope Channel Tester") -root.geometry("600x450") # Set the window size +root.geometry("650x650") # Set the window size # Create an instance of TsDelegate delegate = TsDelegate(root) diff --git a/litepcie/CMakeLists.txt b/litepcie/CMakeLists.txt index 3ebb6f9..2d6c073 100644 --- a/litepcie/CMakeLists.txt +++ b/litepcie/CMakeLists.txt @@ -48,8 +48,8 @@ set_target_properties(litepcie PROPERTIES POSITION_INDEPENDENT_CODE 1) target_include_directories(litepcie PUBLIC public_h include) if(WIN32) - target_link_libraries(litepcie setupapi) + target_link_libraries(litepcie PRIVATE setupapi) elseif(APPLE) - target_link_libraries(litepcie "-framework IOKit") - target_link_libraries(litepcie "-framework CoreFoundation") + target_link_libraries(litepcie "$") + target_link_libraries(litepcie "$") endif() \ No newline at end of file From 6b65179da8f466732ad01d3ffc99ef623ed53817 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 11 Nov 2025 00:51:39 -0500 Subject: [PATCH 75/83] Change ADC Resolution configuration to an enum. Add 12-bit LSB and MSB-justified modes. Add 14-bit Precision mode. --- example/thunderscope_test.cpp | 20 +++++++++++++----- include/thunderscope.h | 4 ++-- include/ts_common.h | 8 +++++++ src/adc.c | 32 +++++++++++++++++++--------- src/adc.h | 4 ++-- src/hmcad15xx.c | 7 +++++-- src/hmcad15xx.h | 2 +- src/thunderscope.c | 6 +++--- src/ts_channel.c | 39 +++++++++++++++++++++++++++-------- src/ts_channel.h | 4 ++-- 10 files changed, 90 insertions(+), 36 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 19ea970..37d206b 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -326,7 +326,8 @@ static void test_io(file_t fd, bool isBeta) } static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, - uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, bool is12bit, bool inRefClk, bool outRefClk, uint32_t refclkFreq) + uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, + bool is12bit, bool is14bit, bool inRefClk, bool outRefClk, uint32_t refclkFreq) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); @@ -375,11 +376,16 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ if(is12bit) { sampleRate = 660000000; - thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, 4096); + thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, TS_12_BIT_MSB); + } + else if(is14bit) + { + sampleRate = 125000000; + thunderscopeSampleModeSet(tsHdl, sampleRate, TS_14_BIT); } else { - thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, 256); + thunderscopeSampleModeSet(tsHdl, sampleRate/numChan, TS_8_BIT); } printf("- Checking HMCAD1520 Sample Rate..."); @@ -706,7 +712,7 @@ int main(int argc, char** argv) uint8_t ac_couple = 0; uint8_t term = 0; bool bitslip = false; - bool mode12bit = false; + bool mode12bit = false, mode14bit = false; bool refInClk = false; bool refOutClk = false; uint32_t refclkFreq = 0; @@ -782,6 +788,10 @@ int main(int argc, char** argv) mode12bit = true; argCount++; break; + case 'p': + mode14bit = true; + argCount++; + break; case '?': fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); print_help(); @@ -918,7 +928,7 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit, refInClk, refOutClk, refclkFreq); + test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit, mode14bit, refInClk, refOutClk, refclkFreq); } // Flash test else if(0 == strcmp(arg, "flash")) diff --git a/include/thunderscope.h b/include/thunderscope.h index 2296a06..40086f2 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -91,10 +91,10 @@ int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* conf); * * @param ts Handle to the Thunderscope device * @param rate Sample Rate to collect (samples per second) - * @param resolution Resolution to sample at. 256 or 4096 + * @param mode Sample Capture Mode * @return int32_t TS_STATUS_OK if the Thunderscope was configured */ -int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolution); +int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, tsSampleFormat_t mode); /** * @brief Set the approximate rate at which interrupts will fire diff --git a/include/ts_common.h b/include/ts_common.h index d945310..815b864 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -61,6 +61,14 @@ typedef enum tsChannelTerm_e TS_TERM_50 = 1, } tsChannelTerm_t; +typedef enum tsSampleFormat_e +{ + TS_8_BIT = 0, + TS_12_BIT_LSB, + TS_12_BIT_MSB, + TS_14_BIT +}tsSampleFormat_t; + typedef enum tsRefClockMode_e { TS_REFCLK_NONE = 0, diff --git a/src/adc.c b/src/adc.c index 1f5bd64..7c3bc47 100644 --- a/src/adc.c +++ b/src/adc.c @@ -16,8 +16,9 @@ #include "ts_calibration.h" -#define TS_ADC_DATA_FRAMING_8BIT (0) -#define TS_ADC_DATA_FRAMING_12BIT (1 << CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_OFFSET); +#define TS_ADC_DATA_FRAMING_8BIT (0) +#define TS_ADC_DATA_FRAMING_12BIT_MSB (1 << CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_OFFSET); +#define TS_ADC_DATA_FRAMING_12BIT_LSB (2 << CSR_ADC_HMCAD1520_SAMPLE_BITS_DATA_WIDTH_OFFSET); typedef enum adc_shuffle_e { @@ -27,6 +28,7 @@ typedef enum adc_shuffle_e ADC_12B_SHUFFLE_1CH = 3, ADC_12B_SHUFFLE_2CH = 4, ADC_12B_SHUFFLE_4CH = 5, + ADC_DUAL_8B_SHUFFLE_4CH = 6, } adc_shuffle_t; @@ -156,8 +158,13 @@ int32_t ts_adc_update_channels(ts_adc_t* adc) } else { - - if(activeCount == 1) + if(adc->adcDev.width == HMCAD15_14_BIT) + { + //Set Quad Channel, Dual-8 + adc->adcDev.mode = HMCAD15_PREC_QUAD_CHANNEL; + shuffleMode = ADC_DUAL_8B_SHUFFLE_4CH; + } + else if(activeCount == 1) { adc->adcDev.mode = HMCAD15_SINGLE_CHANNEL; shuffleMode = ((adc->adcDev.width == HMCAD15_8_BIT) ? ADC_8B_SHUFFLE_1CH : ADC_12B_SHUFFLE_1CH); @@ -212,7 +219,7 @@ int32_t ts_adc_run(ts_adc_t* adc, uint8_t en) return TS_STATUS_OK; } -int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t resolution) +int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, tsSampleFormat_t mode) { if(!adc) { @@ -220,15 +227,20 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res } hmcad15xxDataWidth_t data_mode = HMCAD15_8_BIT; uint32_t sample_bits = TS_ADC_DATA_FRAMING_8BIT; - if(resolution == 4096) + if(mode == TS_12_BIT_LSB) { data_mode = HMCAD15_12_BIT; - sample_bits = TS_ADC_DATA_FRAMING_12BIT; + sample_bits = TS_ADC_DATA_FRAMING_12BIT_LSB; } - else if(resolution == 16384) + else if(mode == TS_12_BIT_MSB) + { + data_mode = HMCAD15_12_BIT; + sample_bits = TS_ADC_DATA_FRAMING_12BIT_MSB; + } + else if(mode == TS_14_BIT) { - data_mode = HMCAD15_14_BIT; //Precision mode uses Dual-8 LVDS + data_mode = HMCAD15_14_BIT; sample_bits = TS_ADC_DATA_FRAMING_8BIT; } @@ -241,7 +253,7 @@ int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t res } else { - LOG_ERROR("Failed to set the ADC Sample Mode %d/%d", sample_rate, resolution); + LOG_ERROR("Failed to set the ADC Sample Mode %d/%d", sample_rate, mode); return TS_STATUS_ERROR; } } diff --git a/src/adc.h b/src/adc.h index ef7cec6..b52ecf9 100644 --- a/src/adc.h +++ b/src/adc.h @@ -98,10 +98,10 @@ int32_t ts_adc_run(ts_adc_t* adc, uint8_t en); * * @param adc Pointer to a ADC instance * @param sample_rate Rate of the ADC Sample clock (Hz) - * @param resolution Resolution mode setting for the ADC. + * @param mode Resolution mode setting for the ADC. * @return int32_t TS_STATUS_OK if the mode was applied successfully */ -int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, uint32_t resolution); +int32_t ts_adc_set_sample_mode(ts_adc_t* adc, uint32_t sample_rate, tsSampleFormat_t mode); /** * @brief Set the calibration on the ADC diff --git a/src/hmcad15xx.c b/src/hmcad15xx.c index 055627f..dd61073 100644 --- a/src/hmcad15xx.c +++ b/src/hmcad15xx.c @@ -107,6 +107,7 @@ int32_t hmcad15xx_power_mode(hmcad15xxADC_t* adc, hmcad15xxPower_t power) HMCAD15_DUAL_CH_1_SLP : 0; break; case HMCAD15_QUAD_CHANNEL: + case HMCAD15_PREC_QUAD_CHANNEL: data |= adc->channelCfg[0].active == 0 ? HMCAD15_QUAD_CH_0_SLP : 0; data |= adc->channelCfg[1].active == 0 ? @@ -264,7 +265,7 @@ int32_t hmcad15xx_set_sample_mode(hmcad15xxADC_t* adc, uint32_t sample_rate, hmc if(((adc->mode == HMCAD15_SINGLE_CHANNEL) && (sample_rate < HMCAD15_SINGLE_LOW_CLK_THRESHOLD)) || ((adc->mode == HMCAD15_DUAL_CHANNEL) && (sample_rate < HMCAD15_DUAL_LOW_CLK_THRESHOLD)) || ((adc->mode == HMCAD15_QUAD_CHANNEL) && (sample_rate < HMCAD15_QUAD_LOW_CLK_THRESHOLD)) || - ((adc->mode == HMCAD15_14BIT_QUAD_CHANNEL) && (sample_rate < HMCAD15_PREC_LOW_CLK_THRESHOLD))) + ((adc->mode == HMCAD15_PREC_QUAD_CHANNEL) && (sample_rate < HMCAD15_PREC_LOW_CLK_THRESHOLD))) { adc->low_clk = 1; } @@ -340,7 +341,7 @@ static void hmcad15xxApplySampleMode(hmcad15xxADC_t* adc) data = HMCAD15_SAMPLE_MODE_SET(adc->mode) | HMCAD15_CLK_DIV_SET(adc->clockDiv); break; - case HMCAD15_14BIT_QUAD_CHANNEL: + case HMCAD15_PREC_QUAD_CHANNEL: adc->clockDiv = HMCAD15_CLK_DIV_1; data = HMCAD15_SAMPLE_MODE_SET(adc->mode) | HMCAD15_SAMPLE_MODE_PREC | @@ -372,6 +373,7 @@ static void hmcad15xxApplyChannelMap(hmcad15xxADC_t* adc) HMCAD15_CH_INVERT_D2(adc->channelCfg[1].invert); break; case HMCAD15_QUAD_CHANNEL: + case HMCAD15_PREC_QUAD_CHANNEL: in12 = HMCAD15_SEL_CH_1(adc->channelCfg[0].input); in12 |= HMCAD15_SEL_CH_2(adc->channelCfg[1].input); in34 = HMCAD15_SEL_CH_3(adc->channelCfg[2].input); @@ -407,6 +409,7 @@ static void hmcad15xxApplyChannelGain(hmcad15xxADC_t* adc) hmcad15xxRegWrite(adc, HMCAD15_REG_COARSE_GAIN_2, cgain); break; case HMCAD15_QUAD_CHANNEL: + case HMCAD15_PREC_QUAD_CHANNEL: cgain = HMCAD15_CGAIN_Q1(adc->channelCfg[0].coarse); cgain |= HMCAD15_CGAIN_Q2(adc->channelCfg[1].coarse); cgain |= HMCAD15_CGAIN_Q3(adc->channelCfg[2].coarse); diff --git a/src/hmcad15xx.h b/src/hmcad15xx.h index 9e8dc0e..2676c7d 100644 --- a/src/hmcad15xx.h +++ b/src/hmcad15xx.h @@ -176,7 +176,7 @@ typedef enum hmcad15xxMode_e HMCAD15_SINGLE_CHANNEL = 1, HMCAD15_DUAL_CHANNEL = 2, HMCAD15_QUAD_CHANNEL = 4, - HMCAD15_14BIT_QUAD_CHANNEL = 8 + HMCAD15_PREC_QUAD_CHANNEL = 8 } hmcad15xxMode_t; typedef enum hmcad15xxDataWidth_e diff --git a/src/thunderscope.c b/src/thunderscope.c index 1471c42..e69f27d 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -243,17 +243,17 @@ int32_t thunderscopeStatusGet(tsHandle_t ts, tsScopeState_t* state) return TS_STATUS_OK; } -int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, uint32_t resolution) +int32_t thunderscopeSampleModeSet(tsHandle_t ts, uint32_t rate, tsSampleFormat_t mode) { ts_inst_t* pInst = (ts_inst_t*)ts; if(pInst && pInst->initialized) { - int32_t status = ts_channel_sample_rate_set(pInst->pChannel, rate, resolution); + int32_t status = ts_channel_sample_rate_set(pInst->pChannel, rate, mode); if(status == TS_STATUS_OK) { //Target 100Hz interrupt rate - pInst->samples.interrupt_rate = 1 + ((((resolution == 256) ? rate : 2*rate) / + pInst->samples.interrupt_rate = 1 + ((((mode == TS_8_BIT) ? rate : 2*rate) / (DMA_BUFFER_SIZE)) / pInst->interrupt_rate); LOG_DEBUG("DMA Interrupt Rate is 1/%d MB", pInst->samples.interrupt_rate); diff --git a/src/ts_channel.c b/src/ts_channel.c index 6f5029f..de96293 100644 --- a/src/ts_channel.c +++ b/src/ts_channel.c @@ -50,6 +50,7 @@ typedef struct ts_channel_s { gpio_t afe_power; gpio_t acq_power; file_t ctrl_handle; + tsSampleFormat_t sampleMode; tsScopeState_t status; } ts_channel_t; @@ -495,7 +496,7 @@ static int32_t ts_channel_update_params(ts_channel_t* pTsHdl, uint32_t chanIdx, } //Update Sample Rate - retVal = ts_channel_sample_rate_set((tsChannelHdl_t)pTsHdl, pTsHdl->status.adc_sample_rate, pTsHdl->status.adc_sample_resolution); + retVal = ts_channel_sample_rate_set((tsChannelHdl_t)pTsHdl, pTsHdl->status.adc_sample_rate, pTsHdl->sampleMode); } return retVal; @@ -550,7 +551,7 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels) return pTsHdl->status; } -int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution) +int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, tsSampleFormat_t mode) { if(tsChannels == NULL) { @@ -560,16 +561,34 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin uint64_t actual_rate = 0; uint64_t max_rate = 0; - if(resolution == 256) + + switch(mode) + { + case TS_8_BIT: { max_rate = TS_MAX_8BIT_SAMPLE_RATE; + ts->status.adc_sample_resolution = 256; + break; } - else if(resolution == 4096) + case TS_12_BIT_LSB: { max_rate = TS_MAX_12BIT_SAMPLE_RATE; + ts->status.adc_sample_resolution = 4096; + break; } - else + case TS_12_BIT_MSB: + { + max_rate = TS_MAX_12BIT_SAMPLE_RATE; + ts->status.adc_sample_resolution = 65536; + break; + } + case TS_14_BIT: { + max_rate = TS_MAX_14BIT_SAMPLE_RATE; + ts->status.adc_sample_resolution = 65536; + break; + } + default: return TS_INVALID_PARAM; } @@ -578,7 +597,10 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin return TS_INVALID_PARAM; } - if(ts->adc.adcDev.mode == HMCAD15_SINGLE_CHANNEL) + ts->sampleMode = mode; + + // Use 1:1 rate for precision mode (14_bit) + if((ts->adc.adcDev.mode == HMCAD15_SINGLE_CHANNEL) || (ts->sampleMode == TS_14_BIT)) { actual_rate = rate; } @@ -625,10 +647,9 @@ int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uin } ts->status.adc_sample_rate = rate; - ts->status.adc_sample_resolution = resolution; - ts->status.adc_sample_bits = resolution == 256 ? 8 : 16; + ts->status.adc_sample_bits = (mode == TS_8_BIT) ? 8 : 16; - ts_adc_set_sample_mode(&ts->adc, rate, resolution); + ts_adc_set_sample_mode(&ts->adc, rate, mode); ts_adc_run(&ts->adc, ts->status.adc_state); return TS_STATUS_OK; diff --git a/src/ts_channel.h b/src/ts_channel.h index 32cc0d0..6575d91 100644 --- a/src/ts_channel.h +++ b/src/ts_channel.h @@ -81,10 +81,10 @@ tsScopeState_t ts_channel_scope_status(tsChannelHdl_t tsChannels); * * @param tsChannels Thunderscope Channel handle * @param rate Samples per Second - * @param resolution Number of bits in each sample. Valid values are 2^8 (256) and 2^12 (4096). + * @param mode Sample Mode to set the resolution of each sample. * @return int32_t TS_STATUS_OK on success, else TS_STATUS_ERROR */ -int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, uint32_t resolution); +int32_t ts_channel_sample_rate_set(tsChannelHdl_t tsChannels, uint32_t rate, tsSampleFormat_t mode); /** * @brief Configure the Clock Generator Reference Clock In/Out From 912c9ef028dd6c14214b86a2470d6b50679cb373 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 16 Dec 2025 00:15:45 -0500 Subject: [PATCH 76/83] Draft of initial Event/Sync API. Include simulated event generator for testing --- example/thunderscope_test.cpp | 9 +++- include/thunderscope.h | 39 +++++++++++++++ include/ts_common.h | 19 ++++++++ src/thunderscope.c | 92 +++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 1 deletion(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 37d206b..3bf3674 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -408,6 +408,8 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ if(rate > 0) { uint64_t data_sum = 0; + uint64_t sample_number = 0; + tsEvent_t event; //Start Sample capture thunderscopeDataEnable(tsHdl, 1); litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); @@ -419,7 +421,7 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { uint32_t readReq = (TS_SAMPLE_BUFFER_SIZE * 0x100); //Collect Samples - int32_t readRes = thunderscopeRead(tsHdl, sampleBuffer, readReq); + int32_t readRes = thunderscopeReadCount(tsHdl, sampleBuffer, readReq, &sample_number); if(readRes < 0) { printf("ERROR: Sample Get Buffers failed with %" PRIi32, readRes); @@ -428,6 +430,11 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { printf("WARN: Read returned different number of bytes for loop %" PRIu32 ", %" PRIu32 " / %" PRIu32 "\r\n", loop, readRes, readReq); } + thunderscopeEventGet(tsHdl, &event); + if(event.ID != TS_EVT_NONE) + { + printf("Found EXT Event at sample %lld\r\n", event.event_sample); + } data_sum += readReq; sampleLen = readRes; diff --git a/include/thunderscope.h b/include/thunderscope.h index 40086f2..51b4b68 100644 --- a/include/thunderscope.h +++ b/include/thunderscope.h @@ -129,6 +129,45 @@ int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable); */ int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len); +/** + * @brief Read data into a buffer and retrieve sample count + * + * @param ts Handle to the Thunderscope device + * @param buffer Pointer to a buffer to store data samples in + * @param len Length of the data buffer available + * @param count Pointer to store the sample count number of the first sample in the buffer + * @return int32_t Length of the data read into the buffer, or a negative error code + */ +int32_t thunderscopeReadCount(tsHandle_t ts, uint8_t* buffer, uint32_t len, uint64_t* count); + +/** + * @brief Configure External Sync Interface + * + * @param ts Handle to the Thunderscope device + * @param mode Set the sync interface to in, out, or disabled + * @return int32_t TS_STATUS_OK if the mode was set successfully, or a negative error code + */ +int32_t thunderscopeExtSyncConfig(tsHandle_t ts, tsSyncMode_t mode); + +/** + * @brief Assert a Sync Event on the External Sync Interface + * + * @param ts Handle to the Thunderscope device + * @return int32_t TS_STATUS_OK if the sync event was asserted set successfully, or a negative error code + */ +int32_t thunderscopeEventSyncAssert(tsHandle_t ts); + +/** + * @brief Poll for a Event + * + * Event struct will be populated with TS_EVT_NONE if there is no event available. + * + * @param ts Handle to the Thunderscope device + * @param evt Pointer to an Event struct to fill + * @return int32_t TS_STATUS_OK if polled successfully, or a negative error code + */ +int32_t thunderscopeEventGet(tsHandle_t ts, tsEvent_t* evt); + /** * @brief Load a new user firmware onto the Thunderscope * diff --git a/include/ts_common.h b/include/ts_common.h index 815b864..a9d284c 100644 --- a/include/ts_common.h +++ b/include/ts_common.h @@ -138,6 +138,25 @@ typedef struct tsScopeState_s sysHealth_t sys_health; } tsScopeState_t; +typedef enum tsSyncMode_e +{ + TS_SYNC_DISABLED, + TS_SYNC_OUT, + TS_SYNC_IN +} tsSyncMode_t; + +typedef enum tsEventType_e +{ + TS_EVT_NONE = 0, + TS_EVT_HOST_SW, + TS_EVT_EXT_SYNC, +} tsEventType_t; + +typedef struct tsEvent_s +{ + tsEventType_t ID; + uint64_t event_sample; +} tsEvent_t; #ifdef __cplusplus } diff --git a/src/thunderscope.c b/src/thunderscope.c index e69f27d..64d7746 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -43,6 +43,7 @@ typedef struct ts_inst_s bool initialized; ts_fw_manager_t fw; uint32_t interrupt_rate; + uint8_t bytes_per_sample; //TBD - Other Instance Data } ts_inst_t; @@ -138,6 +139,7 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) free(pInst); return NULL; } + pInst->bytes_per_sample = 1; pInst->initialized = true; } @@ -377,6 +379,26 @@ int32_t thunderscopeDataEnable(tsHandle_t ts, uint8_t enable) if(enable) { gpio_group_set(pInst->status_leds, pInst->signals->active); + //Get current sample increment + pInst->bytes_per_sample = ts_channel_scope_status(pInst->pChannel).adc_sample_bits / 8; + uint8_t active_channels = 0; + for(uint8_t ch=0; ch < TS_NUM_CHANNELS; ch++) + { + tsChannelParam_t params; + ts_channel_params_get(pInst->pChannel, ch, ¶ms); + if(params.active) + { + active_channels++; + } + } + if(active_channels == 2) + { + pInst->bytes_per_sample *= 2; + } + else if(active_channels > 2) + { + pInst->bytes_per_sample *= 4; + } } else { @@ -398,6 +420,76 @@ int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) } } +int32_t thunderscopeReadCount(tsHandle_t ts, uint8_t* buffer, uint32_t len, uint64_t* count) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t sample_result = TS_STATUS_ERROR; + if(pInst && pInst->initialized) + { + sample_result = samples_get_buffers(&pInst->samples, buffer, len); + if(sample_result > 0) + { + samples_update_status(&pInst->samples); + *count = ((pInst->samples.driver_buffer_count * DMA_BUFFER_SIZE) - sample_result) / pInst->bytes_per_sample; + } + else + { + *count = 0; + } + } + + return sample_result; +} + +int32_t thunderscopeExtSyncConfig(tsHandle_t ts, tsSyncMode_t mode) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t status = TS_STATUS_ERROR; + if(pInst) + { + //TODO + status = TS_STATUS_OK; + } + + return status; +} + +int32_t thunderscopeEventSyncAssert(tsHandle_t ts) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t status = TS_STATUS_ERROR; + if(pInst) + { + //TODO + status = TS_STATUS_OK; + } + + return status; +} + +int32_t thunderscopeEventGet(tsHandle_t ts, tsEvent_t* evt) +{ + ts_inst_t* pInst = (ts_inst_t*)ts; + int32_t status = TS_STATUS_ERROR; + if(pInst && evt) + { + //TODO + evt->ID = TS_EVT_NONE; + evt->event_sample = 0; + status = TS_STATUS_OK; + + //Debug + static uint64_t lastBuffer = 0; + if(pInst->samples.driver_buffer_count > (lastBuffer + 950)) + { + evt->ID = TS_EVT_EXT_SYNC; + evt->event_sample = ((pInst->samples.driver_buffer_count * DMA_BUFFER_SIZE)) / pInst->bytes_per_sample + 200; + lastBuffer = pInst->samples.driver_buffer_count; + } + } + + return status; +} int32_t thunderscopeFwUpdate(tsHandle_t ts, const char* bitstream, uint32_t len) { From 8df8aee3ed3382e7acb71dc3e0c0537d64217f49 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Fri, 26 Dec 2025 01:32:05 -0500 Subject: [PATCH 77/83] Add Events module --- CMakeLists.txt | 2 + litepcie/public_h/csr.h | 49 +++++++++- src/events.c | 201 ++++++++++++++++++++++++++++++++++++++++ src/events.h | 88 ++++++++++++++++++ src/thunderscope.c | 26 ++++-- 5 files changed, 357 insertions(+), 9 deletions(-) create mode 100644 src/events.c create mode 100644 src/events.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a69997..96143c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ set(TS_SOURCES src/spi.c src/afe.c src/adc.c + src/events.c src/hmcad15xx.c src/lmh6518.c src/mcp4728.c @@ -95,6 +96,7 @@ set(TS_HEADERS src/spi.h src/adc.h src/afe.h + src/events.h src/mcp_clkgen.h src/mcp_zl3026x.h src/platform.h diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index 19a16b4..ad8b946 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-10-18 14:34:05 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-12-25 16:54:50 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -505,4 +505,51 @@ #define CSR_SPIBUS_SPI1_LOOPBACK_MODE_OFFSET 0 #define CSR_SPIBUS_SPI1_LOOPBACK_MODE_SIZE 1 +/* EVENTS Registers */ +#define CSR_EVENTS_BASE (CSR_BASE + 0x9000L) +#define CSR_EVENTS_ENGINE_CONTROL_ADDR (CSR_BASE + 0x9000L) +#define CSR_EVENTS_ENGINE_CONTROL_SIZE 1 +#define CSR_EVENTS_ENGINE_STATUS_ADDR (CSR_BASE + 0x9004L) +#define CSR_EVENTS_ENGINE_STATUS_SIZE 1 +#define CSR_EVENTS_ENGINE_EVENT_ADDR (CSR_BASE + 0x9008L) +#define CSR_EVENTS_ENGINE_EVENT_SIZE 1 +#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_ADDR (CSR_BASE + 0x900cL) +#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_SIZE 1 +#define CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR (CSR_BASE + 0x9010L) +#define CSR_EVENTS_ENGINE_FIFO_READMARKER_SIZE 2 +#define CSR_EVENTS_GENERATOR_CONTROL_ADDR (CSR_BASE + 0x9018L) +#define CSR_EVENTS_GENERATOR_CONTROL_SIZE 1 +#define CSR_EVENTS_GENERATOR_TIMEOUT_ADDR (CSR_BASE + 0x901cL) +#define CSR_EVENTS_GENERATOR_TIMEOUT_SIZE 1 +#define CSR_EVENTS_EXT_SYNC_CONTROL_ADDR (CSR_BASE + 0x9020L) +#define CSR_EVENTS_EXT_SYNC_CONTROL_SIZE 1 +#define CSR_EVENTS_EXT_SYNC_PULSE_LEN_ADDR (CSR_BASE + 0x9024L) +#define CSR_EVENTS_EXT_SYNC_PULSE_LEN_SIZE 1 +#define CSR_EVENTS_EXT_SYNC_STATUS_ADDR (CSR_BASE + 0x9028L) +#define CSR_EVENTS_EXT_SYNC_STATUS_SIZE 1 + +/* EVENTS Fields */ +#define CSR_EVENTS_ENGINE_CONTROL_IN_EN_MASK_OFFSET 0 +#define CSR_EVENTS_ENGINE_CONTROL_IN_EN_MASK_SIZE 12 +#define CSR_EVENTS_ENGINE_CONTROL_OUT_EN_MASK_OFFSET 16 +#define CSR_EVENTS_ENGINE_CONTROL_OUT_EN_MASK_SIZE 12 +#define CSR_EVENTS_ENGINE_CONTROL_EVENT_FLUSH_OFFSET 31 +#define CSR_EVENTS_ENGINE_CONTROL_EVENT_FLUSH_SIZE 1 +#define CSR_EVENTS_ENGINE_STATUS_IN_STAT_OFFSET 0 +#define CSR_EVENTS_ENGINE_STATUS_IN_STAT_SIZE 12 +#define CSR_EVENTS_ENGINE_EVENT_PENDING_OFFSET 0 +#define CSR_EVENTS_ENGINE_EVENT_PENDING_SIZE 1 +#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_SOURCE_OFFSET 0 +#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_SOURCE_SIZE 4 +#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_RESERVED_OFFSET 4 +#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_RESERVED_SIZE 4 +#define CSR_EVENTS_GENERATOR_CONTROL_IMMEDIATE_OFFSET 0 +#define CSR_EVENTS_GENERATOR_CONTROL_IMMEDIATE_SIZE 1 +#define CSR_EVENTS_GENERATOR_CONTROL_PERIODIC_OFFSET 1 +#define CSR_EVENTS_GENERATOR_CONTROL_PERIODIC_SIZE 1 +#define CSR_EVENTS_EXT_SYNC_STATUS_EVT_IN_OFFSET 0 +#define CSR_EVENTS_EXT_SYNC_STATUS_EVT_IN_SIZE 1 +#define CSR_EVENTS_EXT_SYNC_STATUS_EVT_OUT_OFFSET 8 +#define CSR_EVENTS_EXT_SYNC_STATUS_EVT_OUT_SIZE 1 + #endif /* ! __GENERATED_CSR_H */ diff --git a/src/events.c b/src/events.c new file mode 100644 index 0000000..6a68627 --- /dev/null +++ b/src/events.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of libtslitex. + * Event Subsystem for the Thunderscope LiteX design + * + * Copyright (C) 2025 / Nate Meyer / nate.devel@gmail.com + * + */ + +#include "events.h" +#include "csr.h" +#include "util.h" + + +#define EVENT_SOURCE_SW (0) +#define EVENT_SOURCE_EXT_SYNC (1) + +#define EVENT_OUTPUT_EXT_SYNC (0) + +#define EXT_SYNC_DISABLED (0x00) +#define EXT_SYNC_INPUT (0x01) +#define EXT_SYNC_OUTPUT (0x02) + +#define EVENT_FIFO_FLUSH (1UL << 31) + + +// Defaults +#define EXT_SYNC_PULSE_WIDTH_DEFAULT (50) +#define EVENT_SOURCE_ENABLE_DEFAULT (1UL << EVENT_SOURCE_SW) + + +static const tsEvent_t g_EventNone = { + .ID = TS_EVT_NONE, + .event_sample = 0 +}; + + +int32_t events_initialize(file_t handle) +{ + int32_t status = TS_STATUS_OK; + uint32_t evt_data; + + // Default Sync pin to Disabled + status = events_set_ext_sync(handle, TS_SYNC_DISABLED); + + // Enable Default Event Sources and disable all Outputs + evt_data = EVENT_SOURCE_ENABLE_DEFAULT; + // Flush the Event queue + evt_data |= EVENT_FIFO_FLUSH; + litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_data); + + // Default Ext Pulse Width + litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_PULSE_LEN_ADDR, EXT_SYNC_PULSE_WIDTH_DEFAULT); + + return status; +} + +bool events_available(file_t handle) +{ + if(handle != INVALID_HANDLE_VALUE && + litepcie_readl(handle, CSR_EVENTS_ENGINE_EVENT_ADDR) & (1UL << CSR_EVENTS_ENGINE_EVENT_PENDING_OFFSET)) + { + return true; + } + else + { + return false; + } +} + +int32_t events_get_next(file_t handle, tsEvent_t *pEvent) +{ + if(handle == INVALID_HANDLE_VALUE) + { + LOG_ERROR("Invalid Event Handle"); + return TS_STATUS_ERROR; + } + else if(pEvent == NULL) + { + LOG_ERROR("Invalid Event pointer"); + return TS_STATUS_ERROR; + } + + uint32_t evt_data = 0; + + // Read Type, then Sample + evt_data = litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR); + if((evt_data & 0xF) == EVENT_SOURCE_SW) + { + pEvent->ID = TS_EVT_HOST_SW; + } + else if((evt_data & 0xF) == EVENT_SOURCE_EXT_SYNC) + { + pEvent->ID = TS_EVT_EXT_SYNC; + } + else + { + LOG_ERROR("Unknown Event Source: %x", evt_data); + return TS_STATUS_ERROR; + } + + pEvent->event_sample = (uint64_t)litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR); + pEvent->event_sample += ((uint64_t)litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR + 4) << 32); + + return TS_STATUS_OK; +} + +int32_t events_set_periodic(file_t handle, uint32_t period_us) +{ + if(handle == INVALID_HANDLE_VALUE) + { + LOG_ERROR("Invalid Event Handle"); + return TS_STATUS_ERROR; + } + + if(period_us == 0) + { + //Disable periodic event generation + litepcie_writel(handle, CSR_EVENTS_GENERATOR_CONTROL_ADDR, 0); + } + else + { + litepcie_writel(handle, CSR_EVENTS_GENERATOR_TIMEOUT_ADDR, period_us); + litepcie_writel(handle, CSR_EVENTS_GENERATOR_CONTROL_ADDR, (1 << CSR_EVENTS_GENERATOR_CONTROL_PERIODIC_OFFSET)); + } + + return TS_STATUS_OK; +} + +int32_t events_set_immediate(file_t handle) +{ + if(handle == INVALID_HANDLE_VALUE) + { + LOG_ERROR("Invalid Event Handle"); + return TS_STATUS_ERROR; + } + + litepcie_writel(handle, CSR_EVENTS_GENERATOR_CONTROL_ADDR, (1 << CSR_EVENTS_GENERATOR_CONTROL_IMMEDIATE_OFFSET)); + return TS_STATUS_OK; +} + +uint32_t events_get_source_status(file_t handle) +{ + return litepcie_readl(handle, CSR_EVENTS_ENGINE_STATUS_ADDR) & 0xFFFF; +} + +int32_t events_set_ext_sync(file_t handle, tsSyncMode_t mode) +{ + if(handle == INVALID_HANDLE_VALUE) + { + LOG_ERROR("Invalid Event Handle"); + return TS_STATUS_ERROR; + } + + int32_t status = TS_STATUS_ERROR; + uint32_t evt_ctrl = litepcie_readl(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR); + + switch (mode) + { + case TS_SYNC_DISABLED: + { + //Disable Ext Output + evt_ctrl &= ~(1UL << EVENT_OUTPUT_EXT_SYNC); + //Disable Ext Input + evt_ctrl &= ~(1UL << EVENT_SOURCE_EXT_SYNC); + litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); + litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_DISABLED); + status = TS_STATUS_OK; + break; + } + case TS_SYNC_IN: + { + //Disable Ext Output + evt_ctrl &= ~(1UL << EVENT_OUTPUT_EXT_SYNC); + //Enable Ext Input + evt_ctrl |= (1UL << EVENT_SOURCE_EXT_SYNC); + litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); + litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_INPUT); + status = TS_STATUS_OK; + break; + } + case TS_SYNC_OUT: + { + //Enable Ext Output + evt_ctrl |= (1UL << EVENT_OUTPUT_EXT_SYNC); + //Disable Ext Input + evt_ctrl &= ~(1UL << EVENT_SOURCE_EXT_SYNC); + litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); + litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_OUTPUT); + status = TS_STATUS_OK; + break; + } + default: + { + LOG_ERROR("Invalid Sync Mode: %x", mode); + break; + } + } + + return status; +} diff --git a/src/events.h b/src/events.h new file mode 100644 index 0000000..c1884e7 --- /dev/null +++ b/src/events.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of libtslitex. + * Event Subsystem for the Thunderscope LiteX design + * + * Copyright (C) 2025 / Nate Meyer / nate.devel@gmail.com + * + */ + +#ifndef EVENTS_H_ +#define EVENTS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ts_common.h" +#include "liblitepcie.h" + + +/** + * @brief Set Initial conditions of the Event Controls + * + * @param handle File handle + * @return int32_t TS_STATUS_OK if the Event controller is initialized successfully + */ +int32_t events_initialize(file_t handle); + +/** + * @brief Check status bit if an Event is pending in the queue + * + * @param handle File handle + * @return bool True if an event can be read, False otherwise + */ +bool events_available(file_t handle); + +/** + * @brief Pull an event off the queue + * + * @param handle File handle + * @param pEvent Pointer to an event structure to populate + * @return int32_t TS_STATUS_OK if the event was retrieved successfully + */ +int32_t events_get_next(file_t handle, tsEvent_t *pEvent); + +/** + * @brief Configure the Event Generator to produce a timed event. The event will repeat with the + * given period. + * + * @param handle File handle + * @param period_us Event period (microseconds) + * @return int32_t TS_STATUS_OK if the event was configured successfully + */ +int32_t events_set_periodic(file_t handle, uint32_t period_us); + +/** + * @brief Immediately trigger an event with the Event Generator + * + * @param handle File handle + * @return int32_t TS_STATUS_OK if the event was configured successfully + */ +int32_t events_set_immediate(file_t handle); + +/** + * @brief Retrieve the current state of the Event Source Inputs to the Event Engine + * + * @param handle File handle + * @return uint32_t Value of the Source Input status + */ +uint32_t events_get_source_status(file_t handle); + +/** + * @brief Configure the External Sync Pin direction + * + * @param handle File handle + * @param mode Sync I/O Mode + * @return int32_t TS_STATUS_OK if the External Sync Pin was configured successfully + */ +int32_t events_set_ext_sync(file_t handle, tsSyncMode_t mode); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/thunderscope.c b/src/thunderscope.c index 64d7746..d892cdf 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -16,6 +16,7 @@ #include "ts_calibration.h" #include "ts_channel.h" #include "samples.h" +#include "events.h" #include "gpio.h" #include "ts_fw_manager.h" #include "ts_data.h" @@ -153,6 +154,8 @@ tsHandle_t thunderscopeOpen(uint32_t devIdx, bool skip_init) return NULL; } + events_initialize(pInst->ctrl); + // Get Factory Build Info ts_data_factory_id_get(&pInst->fw, &pInst->identity); @@ -188,6 +191,7 @@ int32_t thunderscopeClose(tsHandle_t ts) ts_channel_destroy(pInst->pChannel); } + thunderscopeExtSyncConfig(pInst->ctrl, TS_SYNC_DISABLED); gpio_group_set(pInst->status_leds, pInst->signals->disabled); litepcie_close(pInst->ctrl); free(pInst); @@ -447,8 +451,7 @@ int32_t thunderscopeExtSyncConfig(tsHandle_t ts, tsSyncMode_t mode) int32_t status = TS_STATUS_ERROR; if(pInst) { - //TODO - status = TS_STATUS_OK; + status = events_set_ext_sync(pInst->ctrl, mode); } return status; @@ -460,8 +463,7 @@ int32_t thunderscopeEventSyncAssert(tsHandle_t ts) int32_t status = TS_STATUS_ERROR; if(pInst) { - //TODO - status = TS_STATUS_OK; + status = events_set_immediate(pInst->ctrl); } return status; @@ -473,11 +475,18 @@ int32_t thunderscopeEventGet(tsHandle_t ts, tsEvent_t* evt) int32_t status = TS_STATUS_ERROR; if(pInst && evt) { - //TODO - evt->ID = TS_EVT_NONE; - evt->event_sample = 0; - status = TS_STATUS_OK; + if( !events_available(pInst->ctrl) ) + { + evt->ID = TS_EVT_NONE; + evt->event_sample = 0; + status = TS_STATUS_OK; + } + else + { + status = events_get_next(pInst->ctrl, evt); + } + #if 0 //Debug static uint64_t lastBuffer = 0; if(pInst->samples.driver_buffer_count > (lastBuffer + 950)) @@ -486,6 +495,7 @@ int32_t thunderscopeEventGet(tsHandle_t ts, tsEvent_t* evt) evt->event_sample = ((pInst->samples.driver_buffer_count * DMA_BUFFER_SIZE)) / pInst->bytes_per_sample + 200; lastBuffer = pInst->samples.driver_buffer_count; } + #endif } return status; From 2abd2a7f174b586804e5ca7327d8e97586acf399 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Tue, 30 Dec 2025 18:44:09 -0500 Subject: [PATCH 78/83] Fix reading event queue --- src/events.c | 32 +++++++++++++++++++++++--------- src/events.h | 1 + src/thunderscope.c | 10 ++++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/events.c b/src/events.c index 6a68627..9746da6 100644 --- a/src/events.c +++ b/src/events.c @@ -15,7 +15,7 @@ #define EVENT_SOURCE_SW (0) #define EVENT_SOURCE_EXT_SYNC (1) -#define EVENT_OUTPUT_EXT_SYNC (0) +#define EVENT_OUTPUT_EXT_SYNC (16) #define EXT_SYNC_DISABLED (0x00) #define EXT_SYNC_INPUT (0x01) @@ -47,6 +47,7 @@ int32_t events_initialize(file_t handle) evt_data = EVENT_SOURCE_ENABLE_DEFAULT; // Flush the Event queue evt_data |= EVENT_FIFO_FLUSH; + LOG_DEBUG("Set Event Control 0x%X", evt_data); litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_data); // Default Ext Pulse Width @@ -82,9 +83,12 @@ int32_t events_get_next(file_t handle, tsEvent_t *pEvent) } uint32_t evt_data = 0; + + // Read Sample, then Type + pEvent->event_sample = (uint64_t)litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR) << 32; + pEvent->event_sample += (uint64_t)litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR + 4); - // Read Type, then Sample - evt_data = litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR); + evt_data = litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READSOURCE_ADDR); if((evt_data & 0xF) == EVENT_SOURCE_SW) { pEvent->ID = TS_EVT_HOST_SW; @@ -99,9 +103,6 @@ int32_t events_get_next(file_t handle, tsEvent_t *pEvent) return TS_STATUS_ERROR; } - pEvent->event_sample = (uint64_t)litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR); - pEvent->event_sample += ((uint64_t)litepcie_readl(handle, CSR_EVENTS_ENGINE_FIFO_READMARKER_ADDR + 4) << 32); - return TS_STATUS_OK; } @@ -154,6 +155,7 @@ int32_t events_set_ext_sync(file_t handle, tsSyncMode_t mode) int32_t status = TS_STATUS_ERROR; uint32_t evt_ctrl = litepcie_readl(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR); + evt_ctrl &= ~(EVENT_FIFO_FLUSH); switch (mode) { @@ -163,6 +165,7 @@ int32_t events_set_ext_sync(file_t handle, tsSyncMode_t mode) evt_ctrl &= ~(1UL << EVENT_OUTPUT_EXT_SYNC); //Disable Ext Input evt_ctrl &= ~(1UL << EVENT_SOURCE_EXT_SYNC); + LOG_DEBUG("Set Event Control 0x%X", evt_ctrl); litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_DISABLED); status = TS_STATUS_OK; @@ -172,21 +175,32 @@ int32_t events_set_ext_sync(file_t handle, tsSyncMode_t mode) { //Disable Ext Output evt_ctrl &= ~(1UL << EVENT_OUTPUT_EXT_SYNC); + litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); + + //Set SYNC IN + litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_INPUT); + //Enable Ext Input evt_ctrl |= (1UL << EVENT_SOURCE_EXT_SYNC); + LOG_DEBUG("Set Event Control 0x%X", evt_ctrl); litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); - litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_INPUT); status = TS_STATUS_OK; break; } case TS_SYNC_OUT: { - //Enable Ext Output - evt_ctrl |= (1UL << EVENT_OUTPUT_EXT_SYNC); //Disable Ext Input evt_ctrl &= ~(1UL << EVENT_SOURCE_EXT_SYNC); litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); + + //Set SYNC OUT litepcie_writel(handle, CSR_EVENTS_EXT_SYNC_CONTROL_ADDR, EXT_SYNC_OUTPUT); + + //Enable Ext Output + evt_ctrl |= (1UL << EVENT_OUTPUT_EXT_SYNC); + LOG_DEBUG("Set Event Control 0x%X", evt_ctrl); + litepcie_writel(handle, CSR_EVENTS_ENGINE_CONTROL_ADDR, evt_ctrl); + status = TS_STATUS_OK; break; } diff --git a/src/events.h b/src/events.h index c1884e7..5646515 100644 --- a/src/events.h +++ b/src/events.h @@ -18,6 +18,7 @@ extern "C" { #include "ts_common.h" #include "liblitepcie.h" +#define TS_BYTES_PER_SAMPLE_COUNT (128/8) // 128-bit sample bus /** * @brief Set Initial conditions of the Event Controls diff --git a/src/thunderscope.c b/src/thunderscope.c index d892cdf..e8c125a 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -484,6 +484,16 @@ int32_t thunderscopeEventGet(tsHandle_t ts, tsEvent_t* evt) else { status = events_get_next(pInst->ctrl, evt); + + if(status == TS_STATUS_OK) + { + //Adjust Sample Count by Samples/increment + evt->event_sample = evt->event_sample * TS_BYTES_PER_SAMPLE_COUNT / pInst->bytes_per_sample; + } + else + { + evt->event_sample = 0; + } } #if 0 From 43bdff8b2ef49f547dd940c9d14b81b75ed7e7cd Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 31 Dec 2025 00:58:19 -0500 Subject: [PATCH 79/83] Fix close function --- src/thunderscope.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thunderscope.c b/src/thunderscope.c index e8c125a..4a456ed 100644 --- a/src/thunderscope.c +++ b/src/thunderscope.c @@ -191,7 +191,7 @@ int32_t thunderscopeClose(tsHandle_t ts) ts_channel_destroy(pInst->pChannel); } - thunderscopeExtSyncConfig(pInst->ctrl, TS_SYNC_DISABLED); + thunderscopeExtSyncConfig(ts, TS_SYNC_DISABLED); gpio_group_set(pInst->status_leds, pInst->signals->disabled); litepcie_close(pInst->ctrl); free(pInst); From f15a307eb81ccb3ef9725967478c887542656b95 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 31 Dec 2025 00:58:50 -0500 Subject: [PATCH 80/83] Add sync control to capture test --- example/thunderscope_test.cpp | 77 +++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 3bf3674..29e0824 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -51,6 +51,7 @@ #include "../src/ts_channel.h" #include "../src/samples.h" #include "../src/platform.h" +#include "../src/events.h" #include "thunderscope.h" @@ -327,7 +328,7 @@ static void test_io(file_t fd, bool isBeta) static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_t bandwidth, uint32_t volt_scale_uV, int32_t offset_uV, uint8_t ac_couple, uint8_t term, bool watch_bitslip, - bool is12bit, bool is14bit, bool inRefClk, bool outRefClk, uint32_t refclkFreq) + bool is12bit, bool is14bit, bool inRefClk, bool outRefClk, uint32_t refclkFreq, bool wait_for_sync, bool sync_out) { uint8_t numChan = 0; tsHandle_t tsHdl = thunderscopeOpen(idx, false); @@ -407,9 +408,21 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ //Only start taking samples if the rate is non-zero if(rate > 0) { + if(wait_for_sync) + { + thunderscopeExtSyncConfig(tsHdl, TS_SYNC_IN); + } + else if(sync_out) + { + thunderscopeExtSyncConfig(tsHdl, TS_SYNC_OUT); + } + + printf("Capturing data buffers:\r\n"); uint64_t data_sum = 0; uint64_t sample_number = 0; + uint64_t event_sample = 0; tsEvent_t event; + bool sync_found = false; //Start Sample capture thunderscopeDataEnable(tsHdl, 1); litepcie_writel(fd, CSR_ADC_HMCAD1520_CONTROL_ADDR, 1 << CSR_ADC_HMCAD1520_CONTROL_STAT_RST_OFFSET); @@ -417,8 +430,22 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ auto startTime = std::chrono::steady_clock::now(); if(sampleBuffer != NULL) { - for(uint32_t loop=0; loop < 150; loop++) + uint32_t loop=0; + while(!sync_found) { + ++loop; + if(sync_out && loop == 50) + { + // events_set_periodic(fd, 1000000); + thunderscopeEventSyncAssert(tsHdl); + } + + //Force Event at 100 + if(loop == 100) + { + sync_found = true; + } + uint32_t readReq = (TS_SAMPLE_BUFFER_SIZE * 0x100); //Collect Samples int32_t readRes = thunderscopeReadCount(tsHdl, sampleBuffer, readReq, &sample_number); @@ -430,14 +457,26 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ { printf("WARN: Read returned different number of bytes for loop %" PRIu32 ", %" PRIu32 " / %" PRIu32 "\r\n", loop, readRes, readReq); } - thunderscopeEventGet(tsHdl, &event); - if(event.ID != TS_EVT_NONE) - { - printf("Found EXT Event at sample %lld\r\n", event.event_sample); - } + printf("Buffer %d Starts with Sample %lld\r\n", loop, sample_number); + data_sum += readReq; sampleLen = readRes; + if(wait_for_sync || sync_out) + { + thunderscopeEventGet(tsHdl, &event); + while(event.ID != TS_EVT_NONE) + { + printf("Found EXT Event %d at sample %lld\r\n", event.ID, event.event_sample); + event_sample = event.event_sample; + thunderscopeEventGet(tsHdl, &event); + } + if(event_sample != 0 && event_sample < (sample_number + sampleLen)) + { + sync_found = true; + } + } + if(watch_bitslip) { tsScopeState_t scopeState = {0}; @@ -451,6 +490,12 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ printf("Scope State Flags: 0x%08x\r\n", scopeState.flags); } } + + if(wait_for_sync || sync_out) + { + // events_set_periodic(fd, 0); + thunderscopeExtSyncConfig(tsHdl, TS_SYNC_DISABLED); + } } auto endTime = std::chrono::steady_clock::now(); @@ -723,6 +768,8 @@ int main(int argc, char** argv) bool refInClk = false; bool refOutClk = false; uint32_t refclkFreq = 0; + bool extTrigger = false; + bool syncOut = false; struct optparse_long argList[] = { {"dev", 'd', OPTPARSE_REQUIRED}, @@ -736,6 +783,8 @@ int main(int argc, char** argv) {"term", 't', OPTPARSE_NONE}, {"bits", 's', OPTPARSE_NONE}, {"12bit", 'm', OPTPARSE_NONE}, + {"ext", 'e', OPTPARSE_NONE}, + {"sync", 'y', OPTPARSE_NONE}, {0} }; @@ -799,6 +848,14 @@ int main(int argc, char** argv) mode14bit = true; argCount++; break; + case 'e': + extTrigger = true; + argCount++; + break; + case 'y': + syncOut = true; + argCount++; + break; case '?': fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); print_help(); @@ -935,7 +992,11 @@ int main(int argc, char** argv) // Setup Channel, record samples to buffer, save buffer to file else if(0 == strcmp(arg, "capture")) { - test_capture(fd, idx, channelBitmap, bandwidth, volt_scale_uV, offset_uV, ac_couple, term, bitslip, mode12bit, mode14bit, refInClk, refOutClk, refclkFreq); + test_capture(fd, idx, channelBitmap, bandwidth, + volt_scale_uV, offset_uV, ac_couple, term, + bitslip, mode12bit, mode14bit, + refInClk, refOutClk, refclkFreq, + extTrigger, syncOut); } // Flash test else if(0 == strcmp(arg, "flash")) From d44961293856f30e54052268762217bcfcb8531a Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 31 Dec 2025 03:12:39 -0500 Subject: [PATCH 81/83] Separate 8 and 16-bit wav processing in capture test --- example/thunderscope_test.cpp | 189 ++++++++++++++++++++++++---------- 1 file changed, 134 insertions(+), 55 deletions(-) diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 29e0824..87d9ede 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -180,6 +180,134 @@ auto awake_time() return now() + 500ms; } +void save_8bit_wav(int8_t* sampleBuffer, uint64_t sampleLen, uint8_t numChan, uint32_t sampleRate) +{ + AudioFile outWav; + outWav.setBitDepth(8); + outWav.setNumChannels(numChan); + if(numChan > 2) + { + outWav.setSampleRate(sampleRate/4); + } + else + { + outWav.setSampleRate(sampleRate/numChan); + } + + AudioFile::AudioBuffer wavBuffer; + wavBuffer.resize(numChan); + if(numChan == 1) + { + wavBuffer[0].resize(sampleLen); + } + else if(numChan == 2) + { + wavBuffer[0].resize(sampleLen/numChan); + wavBuffer[1].resize(sampleLen/numChan); + } + else + { + wavBuffer[0].resize(sampleLen/4); + wavBuffer[1].resize(sampleLen/4); + wavBuffer[2].resize(sampleLen/4); + + if(numChan == 4) + { + wavBuffer[3].resize(sampleLen/4); + } + } + uint64_t sample = 0; + uint64_t idx = 0; + while (idx < sampleLen) + { + wavBuffer[0][sample] = sampleBuffer[idx++]; + if(numChan > 1) + { + wavBuffer[1][sample] = sampleBuffer[idx++]; + if(numChan > 2) + { + wavBuffer[2][sample] = sampleBuffer[idx++]; + if(numChan == 4) + { + wavBuffer[3][sample] = sampleBuffer[idx++]; + } + else + { + idx++; + } + } + } + sample++; + } + outWav.setAudioBuffer(wavBuffer); + outWav.printSummary(); + outWav.save(TS_TEST_WAV_FILE); +} + +void save_16bit_wav(int16_t* sampleBuffer, uint64_t sampleLen, uint8_t numChan, uint32_t sampleRate) +{ + AudioFile outWav; + outWav.setBitDepth(16); + outWav.setNumChannels(numChan); + if(numChan > 2) + { + outWav.setSampleRate(sampleRate/4); + } + else + { + outWav.setSampleRate(sampleRate/numChan); + } + + AudioFile::AudioBuffer wavBuffer; + wavBuffer.resize(numChan); + if(numChan == 1) + { + wavBuffer[0].resize(sampleLen); + } + else if(numChan == 2) + { + wavBuffer[0].resize(sampleLen/numChan); + wavBuffer[1].resize(sampleLen/numChan); + } + else + { + wavBuffer[0].resize(sampleLen/4); + wavBuffer[1].resize(sampleLen/4); + wavBuffer[2].resize(sampleLen/4); + + if(numChan == 4) + { + wavBuffer[3].resize(sampleLen/4); + } + } + uint64_t sample = 0; + uint64_t idx = 0; + while (idx < sampleLen) + { + wavBuffer[0][sample] = sampleBuffer[idx++]; + if(numChan > 1) + { + wavBuffer[1][sample] = sampleBuffer[idx++]; + if(numChan > 2) + { + wavBuffer[2][sample] = sampleBuffer[idx++]; + if(numChan == 4) + { + wavBuffer[3][sample] = sampleBuffer[idx++]; + } + else + { + idx++; + } + } + } + sample++; + } + outWav.setAudioBuffer(wavBuffer); + outWav.printSummary(); + outWav.save(TS_TEST_WAV_FILE); +} + static void test_io(file_t fd, bool isBeta) { printf("\x1b[1m[> Scratch register test:\x1b[0m\n"); @@ -524,66 +652,14 @@ static void test_capture(file_t fd, uint32_t idx, uint8_t channelBitmap, uint16_ outFile.flush(); outFile.close(); - AudioFile outWav; - is12bit ? outWav.setBitDepth(16) : outWav.setBitDepth(8); - outWav.setNumChannels(numChan); - if(numChan > 2) + if(is12bit || is14bit) { - outWav.setSampleRate(sampleRate/4); + save_16bit_wav((int16_t*)sampleBuffer, sampleLen/2, numChan, sampleRate); } else { - outWav.setSampleRate(sampleRate/numChan); - } - - AudioFile::AudioBuffer wavBuffer; - wavBuffer.resize(numChan); - if(numChan == 1) - { - wavBuffer[0].resize(sampleLen); - } - else if(numChan == 2) - { - wavBuffer[0].resize(sampleLen/numChan); - wavBuffer[1].resize(sampleLen/numChan); - } - else - { - wavBuffer[0].resize(sampleLen/4); - wavBuffer[1].resize(sampleLen/4); - wavBuffer[2].resize(sampleLen/4); - - if(numChan == 4) - { - wavBuffer[3].resize(sampleLen/4); - } - } - uint64_t sample = 0; - uint64_t idx = 0; - while (idx < sampleLen) - { - wavBuffer[0][sample] = sampleBuffer[idx++]; - if(numChan > 1) - { - wavBuffer[1][sample] = sampleBuffer[idx++]; - if(numChan > 2) - { - wavBuffer[2][sample] = sampleBuffer[idx++]; - if(numChan == 4) - { - wavBuffer[3][sample] = sampleBuffer[idx++]; - } - else - { - idx++; - } - } - } - sample++; + save_8bit_wav((int8_t*)sampleBuffer, sampleLen, numChan, sampleRate); } - outWav.setAudioBuffer(wavBuffer); - outWav.printSummary(); - outWav.save(TS_TEST_WAV_FILE); } free(sampleBuffer); } @@ -739,6 +815,9 @@ static void print_help(void) printf("\t\t -o Channel Offset [microvolt]\r\n"); printf("\t\t -a AC Couple\r\n"); printf("\t\t -t 50 Ohm termination\r\n"); + printf("\t\t -m 12-bit mode\r\n"); + printf("\t\t -e External Sync Input\r\n"); + printf("\t\t -y External Sync Output\r\n"); printf("\t refclk - run the PLL source with different clock configurations\r\n"); printf("\t\t -i Set the Ref IN Clock frequency\r\n"); printf("\t\t -r Set the Ref OUT Clock frequency\r\n"); From c3b02cbc41de0d31fbd8539db62b67a80e8c3fc2 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Wed, 31 Dec 2025 16:40:16 -0500 Subject: [PATCH 82/83] Update python bindings with Event API. --- bindings/python/tslitex.pxd | 30 +++++++++++++++++++++++++++++- bindings/python/tslitex.pyx | 26 ++++++++++++++++++++++++++ litepcie/public_h/csr.h | 4 +--- litepcie/public_h/soc.h | 6 +++--- litepcie/src/litepcie_helpers.c | 3 +-- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/bindings/python/tslitex.pxd b/bindings/python/tslitex.pxd index 8776bd0..ce1c3d4 100644 --- a/bindings/python/tslitex.pxd +++ b/bindings/python/tslitex.pxd @@ -6,7 +6,7 @@ # # Copyright (C) 2025 / Nate Meyer / nate.devel@gmail.com -from libc.stdint cimport uint32_t, int32_t, uint8_t +from libc.stdint cimport uint32_t, int32_t, uint8_t, uint64_t cdef extern from "thunderscope.h": @@ -89,6 +89,26 @@ cdef extern from "thunderscope.h": ctypedef tsScopeState_s tsScopeState_t + cpdef enum tsSyncMode_e: + TS_SYNC_DISABLED + TS_SYNC_OUT + TS_SYNC_IN + + ctypedef tsSyncMode_e tsSyncMode_t + + cpdef enum tsEventType_e: + TS_EVT_NONE + TS_EVT_HOST_SW + TS_EVT_EXT_SYNC + + ctypedef tsEventType_e tsEventType_t + + cdef struct tsEvent_s: + tsEventType_t ID + uint64_t event_sample + + ctypedef tsEvent_s tsEvent_t + int32_t thunderscopeListDevices(uint32_t devIndex, tsDeviceInfo_t* info) tsHandle_t thunderscopeOpen(uint32_t devIdx, bint skip_init) @@ -109,6 +129,14 @@ cdef extern from "thunderscope.h": int32_t thunderscopeRead(tsHandle_t ts, uint8_t* buffer, uint32_t len) nogil + int32_t thunderscopeReadCount(tsHandle_t ts, uint8_t* buffer, uint32_t len, uint64_t* count) nogil + + int32_t thunderscopeExtSyncConfig(tsHandle_t ts, tsSyncMode_t mode) + + int32_t thunderscopeEventSyncAssert(tsHandle_t ts) nogil + + int32_t thunderscopeEventGet(tsHandle_t ts, tsEvent_t* evt) + int32_t thunderscopeFwUpdate(tsHandle_t ts, const char* bitstream, uint32_t len) nogil int32_t thunderscopeUserDataRead(tsHandle_t ts, char* buffer, uint32_t offset, uint32_t readLen) nogil diff --git a/bindings/python/tslitex.pyx b/bindings/python/tslitex.pyx index 887196c..4938de2 100644 --- a/bindings/python/tslitex.pyx +++ b/bindings/python/tslitex.pyx @@ -162,6 +162,7 @@ cdef class Thunderscope: cdef uint8_t _enable cdef tslitex.tsHandle_t _tsHandle cdef public object channel + cdef tslitex.tsSyncMode_t _ext_sync def __cinit__(self, dev_idx: int, skip_init:bool = False): self.channel = [] @@ -170,6 +171,7 @@ cdef class Thunderscope: self._sample_rate = 1000000000 self._sample_mode = 256 self._enable = 0 + self._ext_sync = tslitex.tsSyncMode_t.TS_SYNC_DISABLED self._tsHandle = tslitex.thunderscopeOpen(dev_idx, skip_init) if self._tsHandle == NULL: raise ValueError(f"Failed to Open Thunderscope Device {dev_idx}", dev_idx) @@ -269,6 +271,24 @@ cdef class Thunderscope: retval = tslitex.thunderscopeSampleInterruptRate(self._tsHandle, rate) + @property + def SyncMode(self): + return self._ext_sync + + @SyncMode.setter + def SyncMode(self, mode : tslitex.tsSyncMode_t): + self._ext_sync = mode + tslitex.thunderscopeExtSyncConfig(self._tsHandle, mode) + + def Event(self): + cdef tslitex.tsEvent_t evt + tslitex.thunderscopeEventGet(self._tsHandle, &evt) + return (evt.ID, evt.event_sample) + + def EventTrigger(self): + with nogil: + tslitex.thunderscopeEventSyncAssert(self._tsHandle) + def Enable(self, enable: bool): cdef int32_t retVal = tslitex.thunderscopeDataEnable(self._tsHandle, enable) if retVal != tslitex.TS_STATUS_OK: @@ -280,3 +300,9 @@ cdef class Thunderscope: readLen = tslitex.thunderscopeRead(self._tsHandle, &data[0], dlen) return readLen + def ReadCount(self, uint8_t[:] data not None, dataLen: int): + cdef uint32_t dlen = dataLen + cdef uint64_t sample + with nogil: + readLen = tslitex.thunderscopeReadCount(self._tsHandle, &data[0], dlen, &sample) + return readLen, sample diff --git a/litepcie/public_h/csr.h b/litepcie/public_h/csr.h index ad8b946..33c3153 100644 --- a/litepcie/public_h/csr.h +++ b/litepcie/public_h/csr.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-12-25 16:54:50 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-12-31 15:49:53 //-------------------------------------------------------------------------------- //-------------------------------------------------------------------------------- @@ -541,8 +541,6 @@ #define CSR_EVENTS_ENGINE_EVENT_PENDING_SIZE 1 #define CSR_EVENTS_ENGINE_FIFO_READSOURCE_SOURCE_OFFSET 0 #define CSR_EVENTS_ENGINE_FIFO_READSOURCE_SOURCE_SIZE 4 -#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_RESERVED_OFFSET 4 -#define CSR_EVENTS_ENGINE_FIFO_READSOURCE_RESERVED_SIZE 4 #define CSR_EVENTS_GENERATOR_CONTROL_IMMEDIATE_OFFSET 0 #define CSR_EVENTS_GENERATOR_CONTROL_IMMEDIATE_SIZE 1 #define CSR_EVENTS_GENERATOR_CONTROL_PERIODIC_OFFSET 1 diff --git a/litepcie/public_h/soc.h b/litepcie/public_h/soc.h index a75a0f9..4641054 100644 --- a/litepcie/public_h/soc.h +++ b/litepcie/public_h/soc.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------- -// Auto-generated by LiteX (a1ea5a2f6) on 2025-08-29 22:49:50 +// Auto-generated by LiteX (a1ea5a2f6) on 2025-12-31 15:49:53 //-------------------------------------------------------------------------------- #ifndef __GENERATED_SOC_H #define __GENERATED_SOC_H @@ -10,12 +10,12 @@ #define CONFIG_CPU_FAMILY #define CONFIG_CPU_NAME #define CONFIG_CPU_HUMAN_NAME "Unknown" -#define CONFIG_IDENTIFIER "LitePCIe SoC on ThunderScope DEV (0.2.0-next-66d756e-20250829-dirty) 2025-08-29 22:49:49" +#define CONFIG_IDENTIFIER "ThunderScope DEV (0.4.0) built with LiteX 2024.12 2025-12-31 15:49:52" #define DMA_CHANNELS 1 #define DMA_ADDR_WIDTH 64 #define PCIE_DMA0_READER_INTERRUPT 0 #define PCIE_DMA0_WRITER_INTERRUPT 1 -#define SPIFLASH_PHY_FREQUENCY 37500000 +#define SPIFLASH_PHY_FREQUENCY 18750000 #define SPIFLASH_MODULE_NAME "mx25u6435e" #define SPIFLASH_MODULE_TOTAL_SIZE 8388608 #define SPIFLASH_MODULE_PAGE_SIZE 256 diff --git a/litepcie/src/litepcie_helpers.c b/litepcie/src/litepcie_helpers.c index 0d65d60..be8be0f 100644 --- a/litepcie/src/litepcie_helpers.c +++ b/litepcie/src/litepcie_helpers.c @@ -45,8 +45,7 @@ static void getDeviceName(PWCHAR devName, DWORD maxLen, DWORD devIdx) devData.cbSize = sizeof(devData); if (!SetupDiEnumDeviceInterfaces(hwDevInfo, NULL, &GUID_DEVINTERFACE_litepciedrv, devIdx, &devData)) { - //Print Error - fprintf(stderr, "No Devices Found\n"); + //Device Not Found goto cleanup; } From 8a1b32da4a64b753043a9d4880800e14fe0a7ee3 Mon Sep 17 00:00:00 2001 From: Nate Meyer Date: Thu, 1 Jan 2026 17:47:17 -0500 Subject: [PATCH 83/83] Byte-swap factory data Tags. Fix spiflash readback for unaligned data writes --- example/thunderscope_fw.cpp | 2 +- example/thunderscope_test.cpp | 2 +- src/spiflash.c | 2 +- src/ts_data.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/thunderscope_fw.cpp b/example/thunderscope_fw.cpp index 8fc700d..b15b29a 100644 --- a/example/thunderscope_fw.cpp +++ b/example/thunderscope_fw.cpp @@ -324,7 +324,7 @@ int main(int argc, char** argv) char* data_content = new char[file_size]; file.read(data_content, file_size); - if(TS_STATUS_OK == thunderscopeFactoryProvisionAppendTLV(ts, (uint32_t)(tag[0] + (tag[1] << 8) + (tag[2] << 16) + (tag[3] << 24)), + if(TS_STATUS_OK == thunderscopeFactoryProvisionAppendTLV(ts, (uint32_t)((tag[0] << 24) + (tag[1] << 16) + (tag[2] << 8) + tag[3]), file_size, (const char*)data_content)) { printf("Finished writing the %s item\r\n", tag); diff --git a/example/thunderscope_test.cpp b/example/thunderscope_test.cpp index 87d9ede..ca4ea94 100644 --- a/example/thunderscope_test.cpp +++ b/example/thunderscope_test.cpp @@ -683,7 +683,7 @@ static void flash_test(char* arg, file_t fd) auto outFile = std::fstream(TS_FLASH_DUMP_FILE, std::ios::out | std::ios::binary | std::ios::trunc); uint8_t *flash_data = (uint8_t*) malloc(0x40000); printf("Dumping Flash to file.\nProgress: "); - for(uint32_t address = 0x0000000; address < 0x2000000; address+=0x40000) + for(uint32_t address = 0x0000000; address < 0x800000; address+=0x40000) { spiflash_read(&spiflash, address, flash_data, 0x40000); printf("|"); diff --git a/src/spiflash.c b/src/spiflash.c index a859468..32ca298 100644 --- a/src/spiflash.c +++ b/src/spiflash.c @@ -352,7 +352,7 @@ int32_t spiflash_write(spiflash_dev_t* dev, uint32_t addr, const uint8_t *pData, NS_DELAY(10000); } - get_flash_data(dev, addr+offset, (uint8_t*)r_buf, w_len); + spiflash_read(dev, addr+offset, (uint8_t*)r_buf, w_len); for (j = 0; j < w_len; j++) { if (r_buf[j] != pData[offset+j]) { LOG_ERROR("Error: verify failed at 0x%08x (0x%02x should be 0x%02x)", (uint32_t)(addr+offset+j), r_buf[j], pData[offset+j]); diff --git a/src/ts_data.h b/src/ts_data.h index 87a2e4c..7b50aa7 100644 --- a/src/ts_data.h +++ b/src/ts_data.h @@ -18,7 +18,7 @@ extern "C" { #include "ts_fw_manager.h" -#define TAGSTR(x) (uint32_t)(x[0] + (x[1] << 8) + (x[2] << 16) + (x[3] << 24)) +#define TAGSTR(x) (uint32_t)((x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3]) #define TAG_HWID TAGSTR("HWID") #define TAG_FCAL TAGSTR("FCAL")