diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index c458b7f1..5dd9fca3 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -25,5 +25,23 @@ set(CMAKE_TOOLCHAIN_FILE toolchain-arm-cortex-m.cmake) project (hackrf_firmware_all C) +SET(PATH_HACKRF_FIRMWARE ${CMAKE_CURRENT_LIST_DIR}) +SET(PATH_HACKRF_CPLD_XSVF ${PATH_HACKRF_FIRMWARE}/cpld/sgpio_if/default.xsvf) +SET(PATH_HACKRF ${PATH_HACKRF_FIRMWARE}/..) +SET(PATH_HACKRF_FIRMWARE_COMMON ${PATH_HACKRF_FIRMWARE}/common) +SET(LIBOPENCM3 ${PATH_HACKRF_FIRMWARE}/libopencm3) +SET(PATH_DFU_PY ${PATH_HACKRF_FIRMWARE}/dfu.py) +SET(PATH_XSVF_TOOL ${PATH_HACKRF_FIRMWARE}/tools/cpld_crc.py) +set(PATH_HACKRF_CPLD_DATA_C ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.c) + +include(ExternalProject) +ExternalProject_Add(libopencm3 + SOURCE_DIR "${LIBOPENCM3}" + BUILD_IN_SOURCE true + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "" +) + add_subdirectory(blinky) add_subdirectory(hackrf_usb) diff --git a/firmware/common/cpld_xc2c.c b/firmware/common/cpld_xc2c.c index 4220abb5..d43221b4 100644 --- a/firmware/common/cpld_xc2c.c +++ b/firmware/common/cpld_xc2c.c @@ -26,10 +26,6 @@ #include #include -#define CPLD_XC2C64A_ROWS (98) -#define CPLD_XC2C64A_BITS_IN_ROW (274) -#define CPLD_XC2C64A_BYTES_IN_ROW ((CPLD_XC2C64A_BITS_IN_ROW + 7) / 8) - static const uint8_t cpld_xc2c64a_row_address[CPLD_XC2C64A_ROWS] = { 0x00, 0x40, 0x60, 0x20, 0x30, 0x70, 0x50, 0x10, 0x18, 0x58, 0x78, 0x38, 0x28, 0x68, 0x48, 0x08, 0x0c, 0x4c, 0x6c, 0x2c, 0x3c, 0x7c, 0x5c, 0x1c, 0x14, 0x54, 0x74, 0x34, 0x24, 0x64, 0x44, 0x04, @@ -40,25 +36,6 @@ static const uint8_t cpld_xc2c64a_row_address[CPLD_XC2C64A_ROWS] = { 0x05, 0x45, }; -static const uint8_t cpld_xc2c64a_mask[6][CPLD_XC2C64A_BYTES_IN_ROW] = { - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xf8, 0x1f, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0x1f, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0x07, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, -}; - -static const uint8_t cpld_xc2c64a_row_mask_index[CPLD_XC2C64A_ROWS] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 4, 2, 2, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 2, 2, 5, 2, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, -}; - typedef enum { CPLD_XC2C_IR_INTEST = 0b00000010, CPLD_XC2C_IR_BYPASS = 0b11111111, @@ -121,12 +98,11 @@ static bool cpld_xc2c_jtag_clock(const jtag_t* const jtag, const uint32_t tms, c return gpio_read(jtag->gpio->gpio_tdo); } -static void cpld_xc2c_jtag_shift_ptr(const jtag_t* const jtag, uint8_t* const tdi_tdo, const size_t count) { - for(size_t i=0; i> 3; const size_t bit_n = i & 7; const uint32_t mask = (1U << bit_n); - const bool tms = (i == (count - 1)); const uint32_t tdo = cpld_xc2c_jtag_clock(jtag, tms, tdi_tdo[byte_n] & mask) ? 1 : 0; @@ -135,6 +111,13 @@ static void cpld_xc2c_jtag_shift_ptr(const jtag_t* const jtag, uint8_t* const td } } +static void cpld_xc2c_jtag_shift_ptr(const jtag_t* const jtag, uint8_t* const tdi_tdo, const size_t count) { + if( count > 0 ) { + cpld_xc2c_jtag_shift_ptr_tms(jtag, tdi_tdo, 0, count - 1, false); + cpld_xc2c_jtag_shift_ptr_tms(jtag, tdi_tdo, count - 1, count, true); + } +} + static uint32_t cpld_xc2c_jtag_shift_u32(const jtag_t* const jtag, const uint32_t tms, const uint32_t tdi, const size_t count) { uint32_t tdo = 0; @@ -189,11 +172,18 @@ static uint8_t cpld_xc2c_jtag_shift_ir(const jtag_t* const jtag, const cpld_xc2c return cpld_xc2c_jtag_shift_ir_pause(jtag, ir, 0); } +static void cpld_xc2c_jtag_reset(const jtag_t* const jtag) { + /* Five TMS=1 to reach Test-Logic-Reset from any point in the TAP state diagram. + */ + cpld_xc2c_jtag_shift_u32(jtag, 0b11111, 0, 5); +} + static void cpld_xc2c_jtag_reset_and_idle(const jtag_t* const jtag) { /* Five TMS=1 to reach Test-Logic-Reset from any point in the TAP state diagram. * One TMS=0 to move from Test-Logic-Reset to Run-Test-Idle. */ - cpld_xc2c_jtag_shift_u32(jtag, 0b011111, 0, 6); + cpld_xc2c_jtag_reset(jtag); + cpld_xc2c_jtag_shift_u32(jtag, 0, 0, 1); } static uint32_t cpld_xc2c_jtag_idcode(const jtag_t* const jtag) { @@ -215,6 +205,20 @@ static void cpld_xc2c_jtag_conld(const jtag_t* const jtag) { static void cpld_xc2c_jtag_enable(const jtag_t* const jtag) { cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_ENABLE); + cpld_xc2c_jtag_clocks(jtag, 800); +} + +static void cpld_xc2c_jtag_disable(const jtag_t* const jtag) { + cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_DISABLE); + cpld_xc2c_jtag_clocks(jtag, 100); +} + +static void cpld_xc2c_jtag_sram_write(const jtag_t* const jtag) { + cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_WRITE); +} + +static void cpld_xc2c_jtag_sram_read(const jtag_t* const jtag) { + cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_SRAM_READ); } static uint32_t cpld_xc2c_jtag_bypass(const jtag_t* const jtag, const bool shift_dr) { @@ -265,7 +269,11 @@ static void cpld_xc2c64a_jtag_read_row(const jtag_t* const jtag, uint8_t address cpld_xc2c_jtag_clocks(jtag, 100); } -bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_value) { +bool cpld_xc2c64a_jtag_checksum( + const jtag_t* const jtag, + const cpld_xc2c64a_verify_t* const verify, + uint32_t* const crc_value +) { cpld_xc2c_jtag_reset_and_idle(jtag); if( cpld_xc2c64a_jtag_idcode_ok(jtag) && cpld_xc2c_jtag_read_write_protect(jtag) && @@ -287,9 +295,9 @@ bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_va const size_t address = cpld_xc2c64a_row_address[row]; cpld_xc2c64a_jtag_read_row(jtag, address, dr); - const size_t mask_index = cpld_xc2c64a_row_mask_index[row]; + const size_t mask_index = verify->mask_index[row]; for(size_t i=0; imask[mask_index].value[i]; } /* Important checksum calculation NOTE: @@ -318,3 +326,103 @@ bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_va return false; } + +static void cpld_xc2c64a_jtag_sram_write_row(const jtag_t* const jtag, uint8_t address, const uint8_t* const data) { + uint8_t write[CPLD_XC2C64A_BYTES_IN_ROW]; + memcpy(&write[0], data, sizeof(write)); + + /* Update-IR or Run-Test/Idle -> Shift-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b001, 0b000, 3); + + /* Shift-DR -> Shift-DR */ + cpld_xc2c_jtag_shift_ptr_tms(jtag, &write[0], 0, CPLD_XC2C64A_BITS_IN_ROW, false); + + /* Shift-DR -> Exit1-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b1000000, address, 7); + + /* Exit1-DR -> Update-DR -> Run-Test/Idle */ + cpld_xc2c_jtag_shift_u32(jtag, 0b01, 0b00, 2); +} + +static void cpld_xc2c64a_jtag_sram_read_row(const jtag_t* const jtag, uint8_t* const data, const uint8_t next_address) { + /* Run-Test/Idle -> Shift-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b001, 0b000, 3); + + /* Shift-DR */ + cpld_xc2c_jtag_shift_ptr_tms(jtag, data, 0, CPLD_XC2C64A_BITS_IN_ROW, false); + + /* Shift-DR -> Exit1-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b1000000, next_address, 7); + + /* Weird, non-IEEE1532 compliant path through TAP machine, described in Xilinx + * Programmer Qualification Specification, applicable only to XC2C64/A. + * Exit1-DR -> Pause-DR -> Exit2-DR -> Update-DR -> Run-Test/Idle + */ + cpld_xc2c_jtag_shift_u32(jtag, 0b0110, 0b0000, 4); +} + +static bool cpld_xc2c64a_jtag_sram_compare_row(const jtag_t* const jtag, const uint8_t* const expected, const uint8_t* const mask, const uint8_t next_address) { + /* Run-Test/Idle -> Shift-DR */ + uint8_t read[CPLD_XC2C64A_BYTES_IN_ROW]; + memset(read, 0xff, sizeof(read)); + cpld_xc2c64a_jtag_sram_read_row(jtag, &read[0], next_address); + + bool matched = true; + if( (expected != NULL) && (mask != NULL) ) { + for(size_t i=0; irow[row].data[0]); + } + + cpld_xc2c_jtag_disable(jtag); + cpld_xc2c_jtag_bypass(jtag, false); + cpld_xc2c_jtag_reset(jtag); +} + +bool cpld_xc2c64a_jtag_sram_verify( + const jtag_t* const jtag, + const cpld_xc2c64a_program_t* const program, + const cpld_xc2c64a_verify_t* const verify +) { + cpld_xc2c_jtag_reset_and_idle(jtag); + cpld_xc2c_jtag_enable(jtag); + + cpld_xc2c_jtag_sram_read(jtag); + + /* Tricky loop to read dummy row first, then first address, then loop back to get + * the first row's data. + */ + bool matched = true; + for(size_t address_row=0; address_row<=CPLD_XC2C64A_ROWS; address_row++) { + const int data_row = (int)address_row - 1; + const size_t mask_index = (data_row >= 0) ? verify->mask_index[data_row] : 0; + const uint8_t* const expected = (data_row >= 0) ? &program->row[data_row].data[0] : NULL; + const uint8_t* const mask = (data_row >= 0) ? &verify->mask[mask_index].value[0] : NULL; + const uint8_t next_address = (address_row < CPLD_XC2C64A_ROWS) ? cpld_xc2c64a_row_address[address_row] : 0; + matched &= cpld_xc2c64a_jtag_sram_compare_row(jtag, expected, mask, next_address); + } + + cpld_xc2c_jtag_disable(jtag); + cpld_xc2c_jtag_bypass(jtag, false); + cpld_xc2c_jtag_reset(jtag); + + return matched; +} diff --git a/firmware/common/cpld_xc2c.h b/firmware/common/cpld_xc2c.h index d9b9231b..ccd3c5f1 100644 --- a/firmware/common/cpld_xc2c.h +++ b/firmware/common/cpld_xc2c.h @@ -27,6 +27,44 @@ #include "cpld_jtag.h" -bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_value); +/* Xilinx CoolRunner II XC2C64A bitstream attributes */ +#define CPLD_XC2C64A_ROWS (98) +#define CPLD_XC2C64A_BITS_IN_ROW (274) +#define CPLD_XC2C64A_BYTES_IN_ROW ((CPLD_XC2C64A_BITS_IN_ROW + 7) / 8) + +typedef struct { + uint8_t data[CPLD_XC2C64A_BYTES_IN_ROW]; +} cpld_xc2c64a_row_data_t; + +typedef struct { + cpld_xc2c64a_row_data_t row[CPLD_XC2C64A_ROWS]; +} cpld_xc2c64a_program_t; + +typedef struct { + uint8_t value[CPLD_XC2C64A_BYTES_IN_ROW]; +} cpld_xc2c64a_row_mask_t; + +typedef struct { + cpld_xc2c64a_row_mask_t mask[6]; + uint8_t mask_index[CPLD_XC2C64A_ROWS]; +} cpld_xc2c64a_verify_t; + +bool cpld_xc2c64a_jtag_checksum( + const jtag_t* const jtag, + const cpld_xc2c64a_verify_t* const verify, + uint32_t* const crc_value +); +void cpld_xc2c64a_jtag_sram_write( + const jtag_t* const jtag, + const cpld_xc2c64a_program_t* const program +); +bool cpld_xc2c64a_jtag_sram_verify( + const jtag_t* const jtag, + const cpld_xc2c64a_program_t* const program, + const cpld_xc2c64a_verify_t* const verify +); + +extern const cpld_xc2c64a_program_t cpld_hackrf_program_sram; +extern const cpld_xc2c64a_verify_t cpld_hackrf_verify; #endif/*__CPLD_XC2C_H__*/ diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index e6c827c1..43865bdb 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -25,6 +25,12 @@ project(hackrf_usb C) include(../hackrf-common.cmake) +add_custom_command( + OUTPUT ${PATH_HACKRF_CPLD_DATA_C} + COMMAND ${PATH_XSVF_TOOL} --code ${PATH_HACKRF_CPLD_XSVF} >${PATH_HACKRF_CPLD_DATA_C} + DEPENDS ${PATH_XSVF_TOOL} ${PATH_HACKRF_CPLD_XSVF} +) + set(SRC_M4 hackrf_usb.c "${PATH_HACKRF_FIRMWARE_COMMON}/tuning.c" @@ -48,6 +54,7 @@ set(SRC_M4 "${PATH_HACKRF_FIRMWARE_COMMON}/fault_handler.c" "${PATH_HACKRF_FIRMWARE_COMMON}/cpld_jtag.c" "${PATH_HACKRF_FIRMWARE_COMMON}/cpld_xc2c.c" + "${PATH_HACKRF_CPLD_DATA_C}" "${PATH_HACKRF_FIRMWARE_COMMON}/crc.c" "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/lenval.c" "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/micro.c" diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 8bb8b417..a575963c 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -45,6 +45,7 @@ #include "usb_api_sweep.h" #include "usb_api_transceiver.h" #include "usb_bulk_buffer.h" +#include "cpld_xc2c.h" #include "hackrf-ui.h" @@ -209,6 +210,14 @@ void usb_set_descriptor_by_serial_number(void) } } +static bool cpld_jtag_sram_load(jtag_t* const jtag) { + cpld_jtag_take(jtag); + cpld_xc2c64a_jtag_sram_write(jtag, &cpld_hackrf_program_sram); + const bool success = cpld_xc2c64a_jtag_sram_verify(jtag, &cpld_hackrf_program_sram, &cpld_hackrf_verify); + cpld_jtag_release(jtag); + return success; +} + int main(void) { pin_setup(); enable_1v8_power(); @@ -220,6 +229,10 @@ int main(void) { #endif cpu_clock_init(); + if( !cpld_jtag_sram_load(&jtag_cpld) ) { + // TODO: Fail, do not continue booting. + } + #ifndef DFU_MODE usb_set_descriptor_by_serial_number(); #endif diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index f1cf1ec4..9b94e27c 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -106,7 +106,7 @@ usb_request_status_t usb_vendor_request_cpld_checksum( if (stage == USB_TRANSFER_STAGE_SETUP) { cpld_jtag_take(&jtag_cpld); - const bool checksum_success = cpld_xc2c64a_jtag_checksum(&jtag_cpld, &cpld_crc); + const bool checksum_success = cpld_xc2c64a_jtag_checksum(&jtag_cpld, &cpld_hackrf_verify, &cpld_crc); cpld_jtag_release(&jtag_cpld); if(!checksum_success) {