2
.gitignore
vendored
2
.gitignore
vendored
@ -68,3 +68,5 @@ firmware/cpld/**/*.xst
|
||||
firmware/cpld/**/*.xwbt
|
||||
|
||||
firmware/**/build
|
||||
|
||||
*.pyc
|
||||
|
@ -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_CPLD_BITSTREAM_TOOL ${PATH_HACKRF_FIRMWARE}/tools/cpld_bitstream.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)
|
||||
|
@ -26,10 +26,6 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<count; i++) {
|
||||
static void cpld_xc2c_jtag_shift_ptr_tms(const jtag_t* const jtag, uint8_t* const tdi_tdo, const size_t start, const size_t end, const bool tms) {
|
||||
for(size_t i=start; i<end; i++) {
|
||||
const size_t byte_n = 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; i<CPLD_XC2C64A_BYTES_IN_ROW; i++) {
|
||||
dr[i] &= cpld_xc2c64a_mask[mask_index][i];
|
||||
dr[i] &= verify->mask[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; i<CPLD_XC2C64A_BYTES_IN_ROW; i++) {
|
||||
const uint8_t significant_differences = (read[i] ^ expected[i]) & mask[i];
|
||||
matched &= (significant_differences == 0);
|
||||
}
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
void cpld_xc2c64a_jtag_sram_write(
|
||||
const jtag_t* const jtag,
|
||||
const cpld_xc2c64a_program_t* const program
|
||||
) {
|
||||
cpld_xc2c_jtag_reset_and_idle(jtag);
|
||||
cpld_xc2c_jtag_enable(jtag);
|
||||
|
||||
cpld_xc2c_jtag_sram_write(jtag);
|
||||
|
||||
for(size_t row=0; row<CPLD_XC2C64A_ROWS; row++) {
|
||||
const uint8_t address = cpld_xc2c64a_row_address[row];
|
||||
cpld_xc2c64a_jtag_sram_write_row(jtag, address, &program->row[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;
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -867,3 +867,18 @@ void led_toggle(const led_t led) {
|
||||
void hw_sync_enable(const hw_sync_mode_t hw_sync_mode){
|
||||
gpio_write(&gpio_hw_sync_enable, hw_sync_mode==1);
|
||||
}
|
||||
|
||||
void halt_and_flash(const uint32_t duration) {
|
||||
/* blink LED1, LED2, and LED3 */
|
||||
while (1)
|
||||
{
|
||||
led_on(LED1);
|
||||
led_on(LED2);
|
||||
led_on(LED3);
|
||||
delay(duration);
|
||||
led_off(LED1);
|
||||
led_off(LED2);
|
||||
led_off(LED3);
|
||||
delay(duration);
|
||||
}
|
||||
}
|
@ -312,6 +312,7 @@ void led_toggle(const led_t led);
|
||||
|
||||
void hw_sync_enable(const hw_sync_mode_t hw_sync_mode);
|
||||
|
||||
void halt_and_flash(const uint32_t duration);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -25,6 +25,12 @@ project(hackrf_usb C)
|
||||
|
||||
include(../hackrf-common.cmake)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PATH_HACKRF_CPLD_DATA_C}
|
||||
COMMAND ${PATH_CPLD_BITSTREAM_TOOL} --code ${PATH_HACKRF_CPLD_XSVF} >${PATH_HACKRF_CPLD_DATA_C}
|
||||
DEPENDS ${PATH_CPLD_BITSTREAM_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}/xapp058/lenval.c"
|
||||
"${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/micro.c"
|
||||
"${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/ports.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"
|
||||
|
||||
@ -211,6 +212,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();
|
||||
@ -222,6 +231,10 @@ int main(void) {
|
||||
#endif
|
||||
cpu_clock_init();
|
||||
|
||||
if( !cpld_jtag_sram_load(&jtag_cpld) ) {
|
||||
halt_and_flash(6000000);
|
||||
}
|
||||
|
||||
#ifndef DFU_MODE
|
||||
usb_set_descriptor_by_serial_number();
|
||||
#endif
|
||||
|
@ -61,8 +61,6 @@ static void refill_cpld_buffer(void)
|
||||
|
||||
void cpld_update(void)
|
||||
{
|
||||
#define WAIT_LOOP_DELAY (6000000)
|
||||
int i;
|
||||
int error;
|
||||
|
||||
usb_queue_flush_endpoint(&usb_endpoint_bulk_in);
|
||||
@ -75,20 +73,7 @@ void cpld_update(void)
|
||||
refill_cpld_buffer);
|
||||
if(error == 0)
|
||||
{
|
||||
/* blink LED1, LED2, and LED3 on success */
|
||||
while (1)
|
||||
{
|
||||
led_on(LED1);
|
||||
led_on(LED2);
|
||||
led_on(LED3);
|
||||
for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */
|
||||
__asm__("nop");
|
||||
led_off(LED1);
|
||||
led_off(LED2);
|
||||
led_off(LED3);
|
||||
for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */
|
||||
__asm__("nop");
|
||||
}
|
||||
halt_and_flash(6000000);
|
||||
}else
|
||||
{
|
||||
/* LED3 (Red) steady on error */
|
||||
@ -106,7 +91,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) {
|
||||
|
241
firmware/tools/cpld_bitstream.py
Executable file
241
firmware/tools/cpld_bitstream.py
Executable file
@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Xilinx CoolRunner II XC2C64A characteristics
|
||||
|
||||
bits_of_address = 7
|
||||
bits_of_data = 274
|
||||
bytes_of_data = (bits_of_data + 7) // 8
|
||||
bits_in_program_row = bits_of_address + bits_of_data
|
||||
|
||||
def values_list_line_wrap(values):
|
||||
line_length = 16
|
||||
return [' '.join(values[n:n+line_length]) for n in range(0, len(values), line_length)]
|
||||
|
||||
def dec_lines(bytes):
|
||||
return values_list_line_wrap(['%d,' % n for n in bytes])
|
||||
|
||||
def hex_lines(bytes):
|
||||
return values_list_line_wrap(['0x%02x,' % n for n in bytes])
|
||||
|
||||
def reverse_bits(n, bit_count):
|
||||
byte_count = (bit_count + 7) >> 3
|
||||
# n = int(bytes.hex(), 16)
|
||||
n_bits = bin(n)[2:].zfill(bit_count)
|
||||
n_bits_reversed = n_bits[::-1]
|
||||
n_reversed = int(n_bits_reversed, 2)
|
||||
return n_reversed.to_bytes(byte_count, byteorder='little')
|
||||
|
||||
def extract_addresses(block):
|
||||
return tuple([row['address'] for row in block])
|
||||
|
||||
def extract_data(block):
|
||||
return tuple([row['data'] for row in block])
|
||||
|
||||
def extract_mask(block):
|
||||
return tuple([row['mask'] for row in block])
|
||||
|
||||
def equal_blocks(block1, block2, mask):
|
||||
block1_data = extract_data(block1)
|
||||
block2_data = extract_data(block2)
|
||||
assert(len(block1_data) == len(block2_data))
|
||||
assert(len(block1_data) == len(mask))
|
||||
for row1, row2, mask in zip(block1_data, block2_data, mask):
|
||||
differences = (row1 ^ row2) & mask
|
||||
if differences != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def dump_block(rows, endian='little'):
|
||||
data_bytes = (bits_of_data + 7) >> 3
|
||||
for row in rows:
|
||||
print('%02x %s' % (row['address'], row['data'].to_bytes(data_bytes, byteorder=endian).hex()))
|
||||
|
||||
def extract_programming_data(commands):
|
||||
ir_map = {
|
||||
0x01: 'idcode',
|
||||
0xc0: 'conld',
|
||||
0xe8: 'enable',
|
||||
0xea: 'program',
|
||||
0xed: 'erase',
|
||||
0xee: 'verify',
|
||||
0xf0: 'init',
|
||||
0xff: 'bypass',
|
||||
# Other instructions unimplemented and if encountered, will cause tool to crash.
|
||||
}
|
||||
|
||||
ir = None
|
||||
program = []
|
||||
verify = []
|
||||
for command in commands:
|
||||
if command['type'] == 'xsir':
|
||||
ir = ir_map[command['tdi']['data'][0]]
|
||||
if ir == 'program':
|
||||
program.append([])
|
||||
if ir == 'verify':
|
||||
verify.append([])
|
||||
elif ir == 'verify' and command['type'] == 'xsdrtdo':
|
||||
tdi_length = command['tdi']['length']
|
||||
end_state = command['end_state']
|
||||
if tdi_length == bits_of_address and end_state == 1:
|
||||
address = int(command['tdi']['data'].hex(), 16)
|
||||
verify[-1].append({'address': address})
|
||||
elif tdi_length == bits_of_data and end_state == 0:
|
||||
mask = int(command['tdo_mask']['data'].hex(), 16)
|
||||
expected = int(command['tdo_expected']['data'].hex(), 16)
|
||||
verify[-1][-1]['data'] = expected
|
||||
verify[-1][-1]['mask'] = mask
|
||||
elif ir == 'program' and command['type'] == 'xsdrtdo':
|
||||
tdi_length = command['tdi']['length']
|
||||
end_state = command['end_state']
|
||||
if tdi_length == bits_in_program_row and end_state == 0:
|
||||
tdi = int(command['tdi']['data'].hex(), 16)
|
||||
address = (tdi >> bits_of_data) & ((1 << bits_of_address) - 1)
|
||||
data = tdi & ((1 << bits_of_data) - 1)
|
||||
program[-1].append({
|
||||
'address': address,
|
||||
'data': data
|
||||
})
|
||||
|
||||
return {
|
||||
'program': program,
|
||||
'verify': verify,
|
||||
}
|
||||
|
||||
def validate_programming_data(programming_data):
|
||||
expected_address_sequence = (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, 0x06, 0x46, 0x66, 0x26, 0x36, 0x76, 0x56, 0x16, 0x1e, 0x5e, 0x7e, 0x3e, 0x2e, 0x6e, 0x4e, 0x0e, 0x0a, 0x4a, 0x6a, 0x2a, 0x3a, 0x7a, 0x5a, 0x1a, 0x12, 0x52, 0x72, 0x32, 0x22, 0x62, 0x42, 0x02, 0x03, 0x43, 0x63, 0x23, 0x33, 0x73, 0x53, 0x13, 0x1b, 0x5b, 0x7b, 0x3b, 0x2b, 0x6b, 0x4b, 0x0b, 0x0f, 0x4f, 0x6f, 0x2f, 0x3f, 0x7f, 0x5f, 0x1f, 0x17, 0x57, 0x77, 0x37, 0x27, 0x67, 0x47, 0x07, 0x05, 0x45,)
|
||||
|
||||
# Validate program blocks:
|
||||
|
||||
# There should be two extracted program blocks. The first contains the
|
||||
# the bitstream with done bit(s) not asserted. The second updates the
|
||||
# "done" bit(s) to finish the process.
|
||||
assert(len(programming_data['program']) == 2)
|
||||
|
||||
# First program phase writes the bitstream to flash (or SRAM) with
|
||||
# special bit(s) not asserted, so the bitstream is not yet valid.
|
||||
assert(extract_addresses(programming_data['program'][0]) == expected_address_sequence)
|
||||
|
||||
# Second program phase updates a single row to finish the programming
|
||||
# process.
|
||||
assert(len(programming_data['program'][1]) == 1)
|
||||
assert(programming_data['program'][1][0]['address'] == 0x05)
|
||||
|
||||
# Validate verify blocks:
|
||||
|
||||
# There should be two extracted verify blocks.
|
||||
assert(len(programming_data['verify']) == 2)
|
||||
|
||||
# The two verify blocks should match.
|
||||
assert(programming_data['verify'][0] == programming_data['verify'][1])
|
||||
|
||||
# Check the row address order of the second verify block.
|
||||
assert(extract_addresses(programming_data['verify'][0]) == expected_address_sequence)
|
||||
assert(extract_addresses(programming_data['verify'][1]) == expected_address_sequence)
|
||||
|
||||
# Checks across programming and verification:
|
||||
|
||||
# Check that program data matches data expected during verification.
|
||||
assert(equal_blocks(programming_data['program'][0], programming_data['verify'][0], extract_mask(programming_data['verify'][0])))
|
||||
assert(equal_blocks(programming_data['program'][0], programming_data['verify'][1], extract_mask(programming_data['verify'][1])))
|
||||
|
||||
def make_sram_program(program_blocks):
|
||||
program_sram = list(program_blocks[0])
|
||||
program_sram[-2] = program_blocks[1][0]
|
||||
return program_sram
|
||||
|
||||
#######################################################################
|
||||
# Command line argument parsing.
|
||||
#######################################################################
|
||||
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
action_group = parser.add_mutually_exclusive_group(required=True)
|
||||
action_group.add_argument('--checksum', action='store_true', help='Calculate bitstream read-back CRC32 value')
|
||||
action_group.add_argument('--code', action='store_true', help='Generate C code for bitstream loading/programming/verification')
|
||||
parser.add_argument('--crcmod', action='store_true', help='Use Python crcmod library instead of built-in CRC32 code')
|
||||
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
||||
parser.add_argument('hackrf_xc2c_cpld_xsvf', type=str, help='HackRF Xilinx XC2C64A CPLD XSVF file containing erase/program/verify phases')
|
||||
args = parser.parse_args()
|
||||
|
||||
#######################################################################
|
||||
# Generic XSVF parsing phase, produces a tree of commands performed
|
||||
# against the CPLD.
|
||||
#######################################################################
|
||||
|
||||
with open(args.hackrf_xc2c_cpld_xsvf, "rb") as f:
|
||||
from xsvf import XSVFParser
|
||||
commands = XSVFParser().parse(f, debug=args.debug)
|
||||
|
||||
programming_data = extract_programming_data(commands)
|
||||
validate_programming_data(programming_data)
|
||||
|
||||
#######################################################################
|
||||
# Patch the second programming phase into the first for SRAM
|
||||
# programming.
|
||||
#######################################################################
|
||||
|
||||
verify_blocks = programming_data['verify']
|
||||
program_blocks = programming_data['program']
|
||||
|
||||
#######################################################################
|
||||
# Calculate CRC of data read from CPLD during the second verification
|
||||
# pass, which is after the "done" bit is set. Mask off insignificant
|
||||
# bits (turning them to zero) and extending rows to the next full byte.
|
||||
#######################################################################
|
||||
|
||||
if args.checksum:
|
||||
if args.crcmod:
|
||||
# Use a proper CRC library
|
||||
import crcmod
|
||||
crc = crcmod.predefined.Crc('crc-32')
|
||||
else:
|
||||
# Use my home-grown, simple, slow CRC32 object to avoid additional
|
||||
# Python dependencies.
|
||||
from dumb_crc32 import DumbCRC32
|
||||
crc = DumbCRC32()
|
||||
|
||||
verify_block = verify_blocks[1]
|
||||
for address, data, mask in verify_block:
|
||||
valid_data = data & mask
|
||||
bytes = valid_data.to_bytes(bytes_of_data, byteorder='little')
|
||||
crc.update(bytes)
|
||||
|
||||
print('0x%s' % crc.hexdigest().lower())
|
||||
|
||||
if args.code:
|
||||
program_sram = make_sram_program(program_blocks)
|
||||
verify_block = verify_blocks[1]
|
||||
verify_masks = tuple(frozenset(extract_mask(verify_block)))
|
||||
verify_mask_index = dict([(k, v) for v, k in enumerate(verify_masks)])
|
||||
verify_mask_row_index = [verify_mask_index[row['mask']] for row in verify_block]
|
||||
|
||||
result = []
|
||||
result.extend((
|
||||
'/* WARNING: Auto-generated file. Do not edit. */',
|
||||
'',
|
||||
'#include <cpld_xc2c.h>',
|
||||
'',
|
||||
'const cpld_xc2c64a_program_t cpld_hackrf_program_sram = { {',
|
||||
))
|
||||
data_lines = [', '.join(['0x%02x' % n for n in row['data'].to_bytes(bytes_of_data, byteorder='little')]) for row in program_sram]
|
||||
result.extend(['\t{ { %s } },' % line for line in data_lines])
|
||||
result.extend((
|
||||
'} };',
|
||||
'',
|
||||
'const cpld_xc2c64a_verify_t cpld_hackrf_verify = {',
|
||||
'\t.mask = {',
|
||||
))
|
||||
verify_mask_lines = [', '.join(['0x%02x' % n for n in mask.to_bytes(bytes_of_data, byteorder='little')]) for mask in verify_masks]
|
||||
result.extend(['\t\t{ { %s } },' % line for line in verify_mask_lines])
|
||||
result.extend((
|
||||
'\t},'
|
||||
'\t.mask_index = {',
|
||||
))
|
||||
result.extend(['\t\t%s' % line for line in dec_lines(verify_mask_row_index)])
|
||||
result.extend((
|
||||
'\t}',
|
||||
'};',
|
||||
'',
|
||||
))
|
||||
print('\n'.join(result))
|
22
firmware/tools/dumb_crc32.py
Normal file
22
firmware/tools/dumb_crc32.py
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
class DumbCRC32(object):
|
||||
def __init__(self):
|
||||
self._remainder = 0xffffffff
|
||||
self._reversed_polynomial = 0xedb88320
|
||||
self._final_xor = 0xffffffff
|
||||
|
||||
def update(self, data):
|
||||
bit_count = len(data) * 8
|
||||
for bit_n in range(bit_count):
|
||||
bit_in = data[bit_n >> 3] & (1 << (bit_n & 7))
|
||||
self._remainder ^= 1 if bit_in != 0 else 0
|
||||
bit_out = (self._remainder & 1)
|
||||
self._remainder >>= 1;
|
||||
if bit_out != 0:
|
||||
self._remainder ^= self._reversed_polynomial;
|
||||
|
||||
def digest(self):
|
||||
return self._remainder ^ self._final_xor
|
||||
|
||||
def hexdigest(self):
|
||||
return '%08x' % self.digest()
|
208
firmware/tools/xsvf.py
Normal file
208
firmware/tools/xsvf.py
Normal file
@ -0,0 +1,208 @@
|
||||
|
||||
import struct
|
||||
|
||||
class XSVFParser(object):
|
||||
def __init__(self):
|
||||
self._handlers = {
|
||||
0x00: self.XCOMPLETE ,
|
||||
0x01: self.XTDOMASK ,
|
||||
0x02: self.XSIR ,
|
||||
0x03: self.XSDR ,
|
||||
0x04: self.XRUNTEST ,
|
||||
0x07: self.XREPEAT ,
|
||||
0x08: self.XSDRSIZE ,
|
||||
0x09: self.XSDRTDO ,
|
||||
0x0a: self.XSETSDRMASKS,
|
||||
0x0b: self.XSDRINC ,
|
||||
0x0c: self.XSDRB ,
|
||||
0x0d: self.XSDRC ,
|
||||
0x0e: self.XSDRE ,
|
||||
0x0f: self.XSDRTDOB ,
|
||||
0x10: self.XSDRTDOC ,
|
||||
0x11: self.XSDRTDOE ,
|
||||
0x12: self.XSTATE ,
|
||||
0x13: self.XENDIR ,
|
||||
0x14: self.XENDDR ,
|
||||
0x15: self.XSIR2 ,
|
||||
0x16: self.XCOMMENT ,
|
||||
0x17: self.XWAIT ,
|
||||
}
|
||||
|
||||
def tdomask(self):
|
||||
return self._xtdomask
|
||||
|
||||
def read_byte(self):
|
||||
return self.read_bytes(1)[0]
|
||||
|
||||
def read_bytes(self, n):
|
||||
c = self._f.read(n)
|
||||
if len(c) == n:
|
||||
return c
|
||||
else:
|
||||
raise RuntimeError('unexpected end of file')
|
||||
|
||||
def read_bits(self, n):
|
||||
length_bytes = (n + 7) >> 3
|
||||
return self.read_bytes(length_bytes)
|
||||
|
||||
def read_u32(self):
|
||||
return struct.unpack('>I', self.read_bytes(4))[0]
|
||||
|
||||
def parse(self, f, debug=False):
|
||||
self._f = f
|
||||
self._debug = debug
|
||||
self._xcomplete = False
|
||||
self._xenddr = None
|
||||
self._xendir = None
|
||||
self._xruntest = 0
|
||||
self._xsdrsize = None
|
||||
self._xtdomask = None
|
||||
self._commands = []
|
||||
|
||||
while self._xcomplete == False:
|
||||
self.read_instruction()
|
||||
|
||||
self._f = None
|
||||
|
||||
return self._commands
|
||||
|
||||
def read_instruction(self):
|
||||
instruction_id = self.read_byte()
|
||||
if instruction_id in self._handlers:
|
||||
instruction_handler = self._handlers[instruction_id]
|
||||
result = instruction_handler()
|
||||
if result is not None:
|
||||
self._commands.append(result)
|
||||
else:
|
||||
raise RuntimeError('unexpected instruction 0x%02x' % instruction_id)
|
||||
|
||||
def XCOMPLETE(self):
|
||||
self._xcomplete = True
|
||||
|
||||
def XTDOMASK(self):
|
||||
length_bits = self._xsdrsize
|
||||
self._xtdomask = self.read_bits(length_bits)
|
||||
|
||||
def XSIR(self):
|
||||
length_bits = self.read_byte()
|
||||
tdi = self.read_bits(length_bits)
|
||||
if self._debug:
|
||||
print('XSIR tdi=%d:%s' % (length_bits, tdi.hex()))
|
||||
return {
|
||||
'type': 'xsir',
|
||||
'tdi': {
|
||||
'length': length_bits,
|
||||
'data': tdi
|
||||
},
|
||||
}
|
||||
|
||||
def XSDR(self):
|
||||
length_bits = self._xsdrsize
|
||||
tdi = self.read_bits(length_bits)
|
||||
if self._debug:
|
||||
print('XSDR tdi=%d:%s' % (length_bits, tdi.hex()))
|
||||
return {
|
||||
'type': 'xsdr',
|
||||
'tdi': {
|
||||
'length': length_bits,
|
||||
'data': tdi,
|
||||
},
|
||||
}
|
||||
|
||||
def XRUNTEST(self):
|
||||
self._xruntest = self.read_u32()
|
||||
if self._debug:
|
||||
print('XRUNTEST number=%d' % self._xruntest)
|
||||
|
||||
def XREPEAT(self):
|
||||
repeat = self.read_byte()
|
||||
# print('XREPEAT times=%d' % repeat)
|
||||
|
||||
def XSDRSIZE(self):
|
||||
self._xsdrsize = self.read_u32()
|
||||
|
||||
def XSDRTDO(self):
|
||||
length_bits = self._xsdrsize
|
||||
tdi = self.read_bits(length_bits)
|
||||
tdo_mask = self._xtdomask
|
||||
self._tdo_expected = (length_bits, self.read_bits(length_bits))
|
||||
wait = self._xruntest
|
||||
if wait == 0:
|
||||
end_state = self._xenddr
|
||||
else:
|
||||
end_state = 1 # Run-Test/Idle
|
||||
if self._debug:
|
||||
print('XSDRTDO tdi=%d:%s tdo_mask=%d:%s tdo_expected=%d:%s end_state=%u wait=%u' % (
|
||||
length_bits, tdi.hex(),
|
||||
length_bits, tdo_mask.hex(),
|
||||
self._tdo_expected[0], self._tdo_expected[1].hex(),
|
||||
end_state,
|
||||
wait,
|
||||
))
|
||||
return {
|
||||
'type': 'xsdrtdo',
|
||||
'tdi': {
|
||||
'length': length_bits,
|
||||
'data': tdi
|
||||
},
|
||||
'tdo_mask': {
|
||||
'length': length_bits,
|
||||
'data': tdo_mask,
|
||||
},
|
||||
'tdo_expected': {
|
||||
'length': self._tdo_expected[0],
|
||||
'data': self._tdo_expected[1],
|
||||
},
|
||||
'end_state': end_state,
|
||||
'wait': wait,
|
||||
}
|
||||
|
||||
def XSETSDRMASKS(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRINC(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRB(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRC(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRE(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRTDOB(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRTDOC(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSDRTDOE(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XSTATE(self):
|
||||
state = self.read_byte()
|
||||
if self._debug:
|
||||
print('XSTATE %u' % state)
|
||||
return {
|
||||
'type': 'xstate',
|
||||
'state': state,
|
||||
}
|
||||
|
||||
def XENDIR(self):
|
||||
self._xendir = self.read_byte()
|
||||
|
||||
def XENDDR(self):
|
||||
self._xenddr = self.read_byte()
|
||||
|
||||
def XSIR2(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XCOMMENT(self):
|
||||
raise RuntimeError('unimplemented')
|
||||
|
||||
def XWAIT(self):
|
||||
wait_state = self.read_byte()
|
||||
end_state = self.read_byte()
|
||||
wait_time = self.read_u32()
|
Reference in New Issue
Block a user