diff --git a/firmware/common/fpga.c b/firmware/common/fpga.c index 77febf5a..7519ca68 100644 --- a/firmware/common/fpga.c +++ b/firmware/common/fpga.c @@ -27,6 +27,8 @@ #include "streaming.h" #include "rf_path.h" #include "selftest.h" +#include "fpga.h" +#include "fpga_regs.def" // FPGA bitstreams blob. extern uint32_t _binary_fpga_bin_start; @@ -44,6 +46,47 @@ struct fpga_image_read_ctx { uint8_t buffer[4096 + 2]; }; +uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r) +{ + uint8_t v; + ssp1_set_mode_ice40(); + v = ice40_spi_read(drv->bus, r); + ssp1_set_mode_max283x(); + drv->regs[r] = v; + return v; +} + +void fpga_reg_write(fpga_driver_t* const drv, uint8_t r, uint8_t v) +{ + drv->regs[r] = v; + ssp1_set_mode_ice40(); + ice40_spi_write(drv->bus, r, v); + ssp1_set_mode_max283x(); + FPGA_REG_SET_CLEAN(drv, r); +} + +static inline void fpga_reg_commit(fpga_driver_t* const drv, uint8_t r) +{ + fpga_reg_write(drv, r, drv->regs[r]); +} + +void fpga_regs_commit(fpga_driver_t* const drv) +{ + int r; + for (r = 0; r < FPGA_NUM_REGS; r++) { + if ((drv->regs_dirty >> r) & 0x1) { + fpga_reg_commit(drv, r); + } + } +} + +void fpga_hw_sync_enable(const hw_sync_mode_t hw_sync_mode) +{ + fpga_reg_read(&fpga, FPGA_STANDARD_CTRL); + set_FPGA_STANDARD_CTRL_TRIGGER_EN(&fpga, hw_sync_mode == 1); + fpga_regs_commit(&fpga); +} + static size_t fpga_image_read_block_cb(void* _ctx, uint8_t* out_buffer) { // Assume out_buffer is 4KB @@ -179,9 +222,9 @@ bool fpga_sgpio_selftest() } // Enable PRBS mode. - ssp1_set_mode_ice40(); - ice40_spi_write(&ice40, 0x01, 0x40); - ssp1_set_mode_max283x(); + set_FPGA_STANDARD_CTRL(&fpga, 0); + set_FPGA_STANDARD_CTRL_PRBS(&fpga, 1); + fpga_regs_commit(&fpga); // Stream 512 samples from the FPGA. sgpio_configure(&sgpio_config, SGPIO_DIRECTION_RX); @@ -190,9 +233,8 @@ bool fpga_sgpio_selftest() } // Disable PRBS mode. - ssp1_set_mode_ice40(); - ice40_spi_write(&ice40, 0x01, 0); - ssp1_set_mode_max283x(); + set_FPGA_STANDARD_CTRL_PRBS(&fpga, 0); + fpga_regs_commit(&fpga); // Generate sequence from first value and compare. bool seq_in_sync = true; @@ -272,10 +314,10 @@ bool fpga_if_xcvr_selftest() const size_t num_samples = USB_BULK_BUFFER_SIZE / 2; // Set common RX path and gateware settings for the measurements. - ssp1_set_mode_ice40(); - ice40_spi_write(&ice40, 0x05, 64); // NCO phase increment - ice40_spi_write(&ice40, 0x03, 1); // NCO TX enable - ssp1_set_mode_max283x(); + set_FPGA_STANDARD_TX_PSTEP(&fpga, 128); // NCO phase increment + set_FPGA_STANDARD_TX_CTRL_NCO_EN(&fpga, 1); // NCO TX enable + fpga_regs_commit(&fpga); + rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_RX_CALIBRATION); max2831_set_lna_gain(&max283x, 16); max2831_set_vga_gain(&max283x, 36); @@ -333,9 +375,8 @@ bool fpga_if_xcvr_selftest() sample_rate_set(10000000); rf_path_set_direction(&rf_path, RF_PATH_DIRECTION_OFF); narrowband_filter_set(0); - ssp1_set_mode_ice40(); - ice40_spi_write(&ice40, 0x03, 0); - ssp1_set_mode_max283x(); + set_FPGA_STANDARD_TX_CTRL(&fpga, 0); + fpga_regs_commit(&fpga); if (timeout) { selftest.xcvr_loopback = TIMEOUT; diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h index 11f90789..7acffee9 100644 --- a/firmware/common/fpga.h +++ b/firmware/common/fpga.h @@ -24,6 +24,34 @@ #include +/* Up to 5 registers, each containing up to 8 bits of data */ +#define FPGA_NUM_REGS 5 +#define FPGA_DATA_REGS_MAX_VALUE 255 + +struct fpga_driver_t; +typedef struct fpga_driver_t fpga_driver_t; + +struct fpga_driver_t { + ice40_spi_driver_t* bus; + uint8_t regs[FPGA_NUM_REGS]; + uint8_t regs_dirty; +}; + +/* Read a register via SPI. Save a copy to memory and return + * value. Mark clean. */ +extern uint8_t fpga_reg_read(fpga_driver_t* const drv, uint8_t r); + +/* Write value to register via SPI and save a copy to memory. Mark + * clean. */ +extern void fpga_reg_write(fpga_driver_t* const drv, uint8_t r, uint8_t v); + +/* Write all dirty registers via SPI from memory. Mark all clean. Some + * operations require registers to be written in a certain order. Use + * provided routines for those operations. */ +extern void fpga_regs_commit(fpga_driver_t* const drv); + +void fpga_hw_sync_enable(const hw_sync_mode_t hw_sync_mode); + bool fpga_image_load(unsigned int index); bool fpga_spi_selftest(); bool fpga_sgpio_selftest(); diff --git a/firmware/common/fpga_regs.def b/firmware/common/fpga_regs.def new file mode 100644 index 00000000..1f5a396f --- /dev/null +++ b/firmware/common/fpga_regs.def @@ -0,0 +1,74 @@ +/* -*- mode: c -*- + * + * Copyright 2012 Michael Ossmann + * Copyright 2025 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __FPGA_STANDARD_REGS_DEF +#define __FPGA_STANDARD_REGS_DEF + +#define FPGA_REG_SET_CLEAN(_d, _r) (_d->regs_dirty &= ~(1UL<<_r)) +#define FPGA_REG_SET_DIRTY(_d, _r) (_d->regs_dirty |= (1UL<<_r)) + +/* Generate static inline accessors that operate on the global + * regs. Done this way to (1) allow defs to be scraped out and used + * elsewhere, e.g. in scripts, (2) to avoid dealing with endian + * (structs). This may be used in firmware, or on host predefined + * register loads. */ + +/* On set_, register is always set dirty, even if nothing + * changed. This makes sure that writes that have side effects, + * e.g. frequency setting, are not skipped. */ + +/* n=name, r=regnum, o=offset (bits from LSB) of LSB of field, + * l=length (bits) */ +#define __MREG__(n,r,o,l) \ +static inline uint8_t get_##n(fpga_driver_t* const _d) { \ + return (_d->regs[r] >> o) & ((1L<regs[r] &= (uint8_t)(~(((1L<regs[r] |= (uint8_t)(((v&((1L<setup.index, endpoint->setup.value); - ssp1_set_mode_max283x(); + fpga_reg_write(&fpga, endpoint->setup.index, endpoint->setup.value); usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_OK; } -usb_request_status_t usb_vendor_request_spi_read_fpga( +usb_request_status_t usb_vendor_request_read_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { if (stage == USB_TRANSFER_STAGE_SETUP) { - ssp1_set_mode_ice40(); - const uint8_t value = ice40_spi_read(&ice40, endpoint->setup.index); - ssp1_set_mode_max283x(); + const uint8_t value = fpga_reg_read(&fpga, endpoint->setup.index); endpoint->buffer[0] = value; usb_transfer_schedule_block( endpoint->in, diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index 36ec34b7..8fc62c2c 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -57,11 +57,13 @@ usb_request_status_t usb_vendor_request_set_leds( usb_request_status_t usb_vendor_request_user_config_set_bias_t_opts( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); -usb_request_status_t usb_vendor_request_spi_write_fpga( +#ifdef PRALINE +usb_request_status_t usb_vendor_request_write_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); -usb_request_status_t usb_vendor_request_spi_read_fpga( +usb_request_status_t usb_vendor_request_read_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +#endif #endif /* end of include guard: __USB_API_REGISTER_H__ */ diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index d736ea12..b3045b18 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -417,16 +417,18 @@ int rffc5072_write_register( return result; } -int fpga_spi_read_register(hackrf_device* device, const uint16_t register_number) +int fpga_read_register(hackrf_device* device, const uint16_t register_number) { uint8_t register_value; - int result = - hackrf_fpga_spi_read(device, (uint8_t) register_number, ®ister_value); + int result = hackrf_fpga_read_register( + device, + (uint8_t) register_number, + ®ister_value); if (result == HACKRF_SUCCESS) { printf("[%2d] -> 0x%02x\n", register_number, register_value); } else { - printf("hackrf_fpga_spi_read() failed: %s (%d)\n", + printf("hackrf_fpga_read_register() failed: %s (%d)\n", hackrf_error_name(result), result); } @@ -434,18 +436,36 @@ int fpga_spi_read_register(hackrf_device* device, const uint16_t register_number return result; } -int fpga_spi_write_register( +int fpga_read_registers(hackrf_device* device) +{ + uint16_t register_number; + int result = HACKRF_SUCCESS; + + for (register_number = 1; register_number <= 5; register_number++) { + result = fpga_read_register(device, register_number); + if (result != HACKRF_SUCCESS) { + break; + } + } + + return result; +} + +int fpga_write_register( hackrf_device* device, const uint16_t register_number, const uint16_t register_value) { int result = HACKRF_SUCCESS; - result = hackrf_fpga_spi_write(device, (uint8_t) register_number, register_value); + result = hackrf_fpga_write_register( + device, + (uint8_t) register_number, + register_value); if (result == HACKRF_SUCCESS) { printf("0x%02x -> [%2d]\n", register_value, register_number); } else { - printf("hackrf_fpga_spi_write() failed: %s (%d)\n", + printf("hackrf_fpga_write_register() failed: %s (%d)\n", hackrf_error_name(result), result); } @@ -464,7 +484,7 @@ int read_register(hackrf_device* device, uint8_t part, const uint16_t register_n case PART_RFFC5072: return rffc5072_read_register(device, register_number); case PART_GATEWARE: - return fpga_spi_read_register(device, register_number); + return fpga_read_register(device, register_number); } return HACKRF_ERROR_INVALID_PARAM; } @@ -479,6 +499,8 @@ int read_registers(hackrf_device* device, uint8_t part) return si5351c_read_registers(device); case PART_RFFC5072: return rffc5072_read_registers(device); + case PART_GATEWARE: + return fpga_read_registers(device); } return HACKRF_ERROR_INVALID_PARAM; } @@ -502,7 +524,7 @@ int write_register( case PART_RFFC5072: return rffc5072_write_register(device, register_number, register_value); case PART_GATEWARE: - return fpga_spi_write_register(device, register_number, register_value); + return fpga_write_register(device, register_number, register_value); } return HACKRF_ERROR_INVALID_PARAM; } diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index ff0ee32e..0588dbca 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -101,8 +101,8 @@ typedef enum { HACKRF_VENDOR_REQUEST_SUPPORTED_PLATFORM_READ = 46, HACKRF_VENDOR_REQUEST_SET_LEDS = 47, HACKRF_VENDOR_REQUEST_SET_USER_BIAS_T_OPTS = 48, - HACKRF_VENDOR_REQUEST_FPGA_SPI_WRITE = 49, - HACKRF_VENDOR_REQUEST_FPGA_SPI_READ = 50, + HACKRF_VENDOR_REQUEST_FPGA_WRITE_REG = 49, + HACKRF_VENDOR_REQUEST_FPGA_READ_REG = 50, HACKRF_VENDOR_REQUEST_P2_CTRL = 51, HACKRF_VENDOR_REQUEST_P1_CTRL = 52, HACKRF_VENDOR_REQUEST_SET_NARROWBAND_FILTER = 53, @@ -1169,7 +1169,7 @@ int ADDCALL hackrf_rffc5071_write( } } -int ADDCALL hackrf_fpga_spi_read( +int ADDCALL hackrf_fpga_read_register( hackrf_device* device, uint8_t register_number, uint8_t* value) @@ -1180,7 +1180,7 @@ int ADDCALL hackrf_fpga_spi_read( result = libusb_control_transfer( device->usb_device, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, - HACKRF_VENDOR_REQUEST_FPGA_SPI_READ, + HACKRF_VENDOR_REQUEST_FPGA_READ_REG, 0, register_number, (unsigned char*) value, @@ -1195,7 +1195,7 @@ int ADDCALL hackrf_fpga_spi_read( } } -int ADDCALL hackrf_fpga_spi_write( +int ADDCALL hackrf_fpga_write_register( hackrf_device* device, uint8_t register_number, uint8_t value) @@ -1207,7 +1207,7 @@ int ADDCALL hackrf_fpga_spi_write( device->usb_device, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, - HACKRF_VENDOR_REQUEST_FPGA_SPI_WRITE, + HACKRF_VENDOR_REQUEST_FPGA_WRITE_REG, value, register_number, NULL, diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index ba70ec78..6327e246 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -1574,7 +1574,7 @@ extern ADDAPI int ADDCALL hackrf_rffc5071_write( * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant * @ingroup debug */ -extern ADDAPI int ADDCALL hackrf_fpga_spi_read( +extern ADDAPI int ADDCALL hackrf_fpga_read_register( hackrf_device* device, uint8_t register_number, uint8_t* value); @@ -1591,7 +1591,7 @@ extern ADDAPI int ADDCALL hackrf_fpga_spi_read( * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant * @ingroup debug */ -extern ADDAPI int ADDCALL hackrf_fpga_spi_write( +extern ADDAPI int ADDCALL hackrf_fpga_write_register( hackrf_device* device, uint8_t register_number, uint8_t value);