summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/board/bladerf1/flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/board/bladerf1/flash.c')
-rw-r--r--Radio/HW/BladeRF/src/board/bladerf1/flash.c543
1 files changed, 543 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/board/bladerf1/flash.c b/Radio/HW/BladeRF/src/board/bladerf1/flash.c
new file mode 100644
index 0000000..6850543
--- /dev/null
+++ b/Radio/HW/BladeRF/src/board/bladerf1/flash.c
@@ -0,0 +1,543 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "log.h"
+#include "minmax.h"
+#include "misc.h"
+#include "conversions.h"
+
+#include "bladeRF.h"
+#include "board/board.h"
+
+#include "driver/spi_flash.h"
+
+#include "flash.h"
+
+#define OTP_BUFFER_SIZE 256
+
+int spi_flash_write_fx3_fw(struct bladerf *dev, const uint8_t *image, size_t len)
+{
+ int status;
+ uint8_t *readback_buf;
+ uint8_t *padded_image;
+ uint32_t padded_image_len;
+
+ /* Pad firwmare data out to a page size */
+ const uint32_t page_size = dev->flash_arch->psize_bytes;
+ const uint32_t padding_len =
+ (len % page_size == 0) ? 0 : page_size - (len % page_size);
+
+ /* Flash page where FX3 firmware starts */
+ const uint32_t flash_page_fw = BLADERF_FLASH_ADDR_FIRMWARE /
+ dev->flash_arch->psize_bytes;
+
+ /* Flash erase block where FX3 firmware starts */
+ const uint32_t flash_eb_fw = BLADERF_FLASH_ADDR_FIRMWARE /
+ dev->flash_arch->ebsize_bytes;
+
+ /** Length of firmware region of flash, in erase blocks */
+ const uint32_t flash_eb_len_fw = BLADERF_FLASH_BYTE_LEN_FIRMWARE /
+ dev->flash_arch->ebsize_bytes;
+
+ if (len >= (UINT32_MAX - padding_len)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ padded_image_len = (uint32_t) len + padding_len;
+
+ readback_buf = malloc(padded_image_len);
+ if (readback_buf == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ padded_image = malloc(padded_image_len);
+ if (padded_image == NULL) {
+ free(readback_buf);
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Copy image */
+ memcpy(padded_image, image, len);
+
+ /* Clear the padded region */
+ memset(padded_image + len, 0xFF, padded_image_len - len);
+
+ /* Erase the entire firmware region */
+ status = spi_flash_erase(dev, flash_eb_fw, flash_eb_len_fw);
+ if (status != 0) {
+ log_debug("Failed to erase firmware region: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Convert the image length to pages */
+ padded_image_len /= page_size;
+
+ /* Write the firmware image to flash */
+ status = spi_flash_write(dev, padded_image,
+ flash_page_fw, padded_image_len);
+
+ if (status < 0) {
+ log_debug("Failed to write firmware: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and double-check what we just wrote */
+ status = spi_flash_verify(dev, readback_buf, padded_image,
+ flash_page_fw, padded_image_len);
+ if (status != 0) {
+ log_debug("Flash verification failed: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+error:
+ free(padded_image);
+ free(readback_buf);
+ return status;
+}
+
+static inline void fill_fpga_metadata_page(struct bladerf *dev,
+ uint8_t *metadata,
+ size_t actual_bitstream_len)
+{
+ char len_str[12];
+ int idx = 0;
+
+ memset(len_str, 0, sizeof(len_str));
+ memset(metadata, 0xff, dev->flash_arch->psize_bytes);
+
+ snprintf(len_str, sizeof(len_str), "%u",
+ (unsigned int)actual_bitstream_len);
+
+ binkv_encode_field((char *)metadata, dev->flash_arch->psize_bytes,
+ &idx, "LEN", len_str);
+}
+
+static inline size_t get_flash_eb_len_fpga(struct bladerf *dev)
+{
+ int status;
+ size_t fpga_bytes;
+ size_t eb_count;
+
+ status = dev->board->get_fpga_bytes(dev, &fpga_bytes);
+ if (status < 0) {
+ return status;
+ }
+
+ eb_count = fpga_bytes / dev->flash_arch->ebsize_bytes;
+
+ if ((fpga_bytes % dev->flash_arch->ebsize_bytes) > 0) {
+ // Round up to nearest full block
+ ++eb_count;
+ }
+
+ return eb_count;
+}
+
+#define METADATA_LEN 256
+
+int spi_flash_write_fpga_bitstream(struct bladerf *dev,
+ const uint8_t *bitstream,
+ size_t len)
+{
+ /* Pad data to be page-aligned */
+ const uint32_t page_size = dev->flash_arch->psize_bytes;
+ const uint32_t padding_len =
+ (len % page_size == 0) ? 0 : page_size - (len % page_size);
+
+ /** Flash page where FPGA metadata and bitstream start */
+ const uint32_t flash_page_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->psize_bytes;
+
+ /** Flash erase block where FPGA metadata and bitstream start */
+ const uint32_t flash_eb_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->ebsize_bytes;
+
+ /** Length of entire FPGA region, in units of erase blocks */
+ const uint32_t flash_eb_len_fpga = (uint32_t)get_flash_eb_len_fpga(dev);
+
+ assert(METADATA_LEN <= page_size);
+
+ int status;
+ uint8_t *readback_buf;
+ uint8_t *padded_bitstream;
+ uint8_t metadata[METADATA_LEN];
+ uint32_t padded_bitstream_len;
+
+ if (len >= (UINT32_MAX - padding_len)) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ padded_bitstream_len = (uint32_t)len + padding_len;
+
+ /* Fill in metadata with the *actual* FPGA bitstream length */
+ fill_fpga_metadata_page(dev, metadata, len);
+
+ readback_buf = malloc(padded_bitstream_len);
+ if (readback_buf == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ padded_bitstream = malloc(padded_bitstream_len);
+ if (padded_bitstream == NULL) {
+ free(readback_buf);
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Copy bitstream */
+ memcpy(padded_bitstream, bitstream, len);
+
+ /* Clear the padded region */
+ memset(padded_bitstream + len, 0xFF, padded_bitstream_len - len);
+
+ /* Erase FPGA metadata and bitstream region */
+ status = spi_flash_erase(dev, flash_eb_fpga, flash_eb_len_fpga);
+ if (status != 0) {
+ log_debug("Failed to erase FPGA meta & bitstream regions: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Write the metadata page */
+ status = spi_flash_write(dev, metadata, flash_page_fpga, 1);
+ if (status != 0) {
+ log_debug("Failed to write FPGA metadata page: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Convert the padded bitstream length to pages */
+ padded_bitstream_len /= page_size;
+
+ /* Write the padded bitstream */
+ status = spi_flash_write(dev, padded_bitstream, flash_page_fpga + 1,
+ padded_bitstream_len);
+ if (status != 0) {
+ log_debug("Failed to write bitstream: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and verify metadata */
+ status = spi_flash_verify(dev, readback_buf, metadata, flash_page_fpga, 1);
+ if (status != 0) {
+ log_debug("Failed to verify metadata: %s\n", bladerf_strerror(status));
+ goto error;
+ }
+
+ /* Read back and verify the bitstream data */
+ status = spi_flash_verify(dev, readback_buf, padded_bitstream,
+ flash_page_fpga + 1, padded_bitstream_len);
+ if (status != 0) {
+ log_debug("Failed to verify bitstream data: %s\n",
+ bladerf_strerror(status));
+ goto error;
+ }
+
+error:
+ free(padded_bitstream);
+ free(readback_buf);
+ return status;
+}
+
+int spi_flash_erase_fpga(struct bladerf *dev)
+{
+ int status;
+ size_t fpga_bytes;
+
+ status = dev->board->get_fpga_bytes(dev, &fpga_bytes);
+ if (status < 0) {
+ return status;
+ }
+
+ /** Flash erase block where FPGA metadata and bitstream start */
+ const uint32_t flash_eb_fpga =
+ BLADERF_FLASH_ADDR_FPGA / dev->flash_arch->ebsize_bytes;
+
+ /** Length of entire FPGA region, in units of erase blocks */
+ const uint32_t flash_eb_len_fpga = (uint32_t)get_flash_eb_len_fpga(dev);
+
+ /* Erase the entire FPGA region, including both autoload metadata and the
+ * actual bitstream data */
+ return spi_flash_erase(dev, flash_eb_fpga, flash_eb_len_fpga);
+}
+
+int spi_flash_read_otp(struct bladerf *dev, char *field,
+ char *data, size_t data_size)
+{
+ int status;
+ char otp[OTP_BUFFER_SIZE];
+
+ memset(otp, 0xff, OTP_BUFFER_SIZE);
+
+ status = dev->backend->get_otp(dev, otp);
+ if (status < 0)
+ return status;
+ else
+ return binkv_decode_field(otp, OTP_BUFFER_SIZE, field, data, data_size);
+}
+
+int spi_flash_read_cal(struct bladerf *dev, char *field,
+ char *data, size_t data_size)
+{
+ int status;
+ char cal[CAL_BUFFER_SIZE];
+
+ status = dev->backend->get_cal(dev, cal);
+ if (status < 0)
+ return status;
+ else
+ return binkv_decode_field(cal, CAL_BUFFER_SIZE, field, data, data_size);
+}
+
+int spi_flash_read_serial(struct bladerf *dev, char *serial_buf)
+{
+ int status;
+
+ status = spi_flash_read_otp(dev, "S", serial_buf, BLADERF_SERIAL_LENGTH - 1);
+
+ if (status < 0) {
+ log_info("Unable to fetch serial number. Defaulting to 0's.\n");
+ memset(dev->ident.serial, '0', BLADERF_SERIAL_LENGTH - 1);
+
+ /* Treat this as non-fatal */
+ status = 0;
+ }
+
+ serial_buf[BLADERF_SERIAL_LENGTH - 1] = '\0';
+
+ return status;
+}
+
+int spi_flash_read_vctcxo_trim(struct bladerf *dev, uint16_t *dac_trim)
+{
+ int status;
+ bool ok;
+ int16_t trim;
+ char tmp[7] = { 0 };
+
+ status = spi_flash_read_cal(dev, "DAC", tmp, sizeof(tmp) - 1);
+ if (status < 0) {
+ return status;
+ }
+
+ trim = str2uint(tmp, 0, 0xffff, &ok);
+ if (ok == false) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ *dac_trim = trim;
+
+ return 0;
+}
+
+int spi_flash_read_fpga_size(struct bladerf *dev, bladerf_fpga_size *fpga_size)
+{
+ int status;
+ char tmp[7] = { 0 };
+
+ status = spi_flash_read_cal(dev, "B", tmp, sizeof(tmp) - 1);
+ if (status < 0) {
+ return status;
+ }
+
+ if (!strcmp("40", tmp)) {
+ *fpga_size = BLADERF_FPGA_40KLE;
+ } else if(!strcmp("115", tmp)) {
+ *fpga_size = BLADERF_FPGA_115KLE;
+ } else if(!strcmp("A4", tmp)) {
+ *fpga_size = BLADERF_FPGA_A4;
+ } else if(!strcmp("A5", tmp)) {
+ *fpga_size = BLADERF_FPGA_A5;
+ } else if(!strcmp("A9", tmp)) {
+ *fpga_size = BLADERF_FPGA_A9;
+ } else {
+ *fpga_size = BLADERF_FPGA_UNKNOWN;
+ }
+
+ return status;
+}
+
+int spi_flash_read_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did)
+{
+ int status;
+
+ status = dev->backend->get_flash_id(dev, mid, did);
+
+ return status;
+}
+
+int spi_flash_decode_flash_architecture(struct bladerf *dev,
+ bladerf_fpga_size *fpga_size)
+{
+ int status;
+ struct bladerf_flash_arch *flash_arch;
+
+ status = 0;
+ flash_arch = dev->flash_arch;
+
+ /* Fill in defaults */
+ flash_arch->tsize_bytes = 32 << 17; /* 32 Mbit */
+ flash_arch->psize_bytes = 256;
+ flash_arch->ebsize_bytes = 64 << 10; /* 64 Kbyte */
+ flash_arch->status = STATUS_ASSUMED;
+
+ /* First try to decode the MID/DID of the flash chip */
+ switch( flash_arch->manufacturer_id ) {
+ case 0xC2: /* MACRONIX */
+ log_verbose( "Found SPI flash manufacturer: MACRONIX.\n" );
+ switch( flash_arch->device_id ) {
+ case 0x36:
+ log_verbose( "Found SPI flash device: MX25U3235E (32 Mbit).\n" );
+ flash_arch->tsize_bytes = 32 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ default:
+ log_debug( "Unknown Macronix flash device ID.\n" );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ break;
+
+ case 0xEF: /* WINBOND */
+ log_verbose( "Found SPI flash manufacturer: WINBOND.\n" );
+ switch( flash_arch->device_id ) {
+ case 0x15:
+ log_verbose( "Found SPI flash device: W25Q32JV (32 Mbit).\n" );
+ flash_arch->tsize_bytes = 32 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ case 0x16:
+ log_verbose( "Found SPI flash device: W25Q64JV (64 Mbit).\n" );
+ flash_arch->tsize_bytes = 64 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ case 0x17:
+ log_verbose( "Found SPI flash device: W25Q128JV (128 Mbit).\n" );
+ flash_arch->tsize_bytes = 128 << 17;
+ flash_arch->status = STATUS_SUCCESS;
+ break;
+ default:
+ log_debug( "Unknown Winbond flash device ID [0x%02X].\n" , flash_arch->device_id );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ break;
+
+ default:
+ log_debug( "Unknown flash manufacturer ID.\n" );
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Could not decode flash MID/DID, so assume based on FPGA size */
+ if( status < 0 || flash_arch->status != STATUS_SUCCESS ) {
+ if( (fpga_size == NULL) || (*fpga_size == BLADERF_FPGA_UNKNOWN) ) {
+ log_debug( "Could not decode flash manufacturer/device ID and have "
+ "an unknown FPGA size. Assume default flash "
+ "architecture.\n" );
+ } else {
+ switch( *fpga_size ) {
+ case BLADERF_FPGA_A9:
+ flash_arch->tsize_bytes = 128 << 17;
+ break;
+ default:
+ flash_arch->tsize_bytes = 32 << 17;
+ }
+ log_debug( "Could not decode flash manufacturer/device ID, but "
+ "found a %u kLE FPGA. Setting the most probable "
+ "flash architecture.\n", *fpga_size );
+ }
+ }
+
+ flash_arch->num_pages = flash_arch->tsize_bytes / flash_arch->psize_bytes;
+ flash_arch->num_ebs = flash_arch->tsize_bytes / flash_arch->ebsize_bytes;
+
+ log_verbose("SPI flash total size = %u Mbit\n", (flash_arch->tsize_bytes >> 17));
+ log_verbose("SPI flash page size = %u bytes\n", flash_arch->psize_bytes);
+ log_verbose("SPI flash erase block size = %u bytes\n", flash_arch->ebsize_bytes);
+ log_verbose("SPI flash number of pages = %u\n", flash_arch->num_pages);
+ log_verbose("SPI flash number of erase blocks = %u pages\n", flash_arch->num_ebs);
+
+ return status;
+}
+
+
+int binkv_decode_field(char *ptr, int len, char *field,
+ char *val, size_t maxlen)
+{
+ int c;
+ unsigned char *ub, *end;
+ unsigned short a1, a2;
+ size_t flen, wlen;
+
+ flen = strlen(field);
+
+ ub = (unsigned char *)ptr;
+ end = ub + len;
+ while (ub < end) {
+ c = *ub;
+
+ if (c == 0xff) // flash and OTP are 0xff if they've never been written to
+ break;
+
+ a1 = LE16_TO_HOST(*(unsigned short *)(&ub[c+1])); // read checksum
+ a2 = zcrc(ub, c+1); // calculate checksum
+
+ if (a1 == a2) {
+ if (!strncmp((char *)ub + 1, field, flen)) {
+ wlen = min_sz(c - flen, maxlen);
+ strncpy(val, (char *)ub + 1 + flen, wlen);
+ val[wlen] = 0;
+ return 0;
+ }
+ } else {
+ log_debug( "%s: Field checksum mismatch\n", __FUNCTION__);
+ return BLADERF_ERR_INVAL;
+ }
+ ub += c + 3; //skip past `c' bytes, 2 byte CRC field, and 1 byte len field
+ }
+ return BLADERF_ERR_INVAL;
+}
+
+int binkv_encode_field(char *ptr, int len, int *idx,
+ const char *field, const char *val)
+{
+ int vlen, flen, tlen;
+ flen = (int)strlen(field);
+ vlen = (int)strlen(val);
+ tlen = flen + vlen + 1;
+
+ if (tlen >= 256 || *idx + tlen >= len)
+ return BLADERF_ERR_MEM;
+
+ ptr[*idx] = flen + vlen;
+ strcpy(&ptr[*idx + 1], field);
+ strcpy(&ptr[*idx + 1 + flen], val);
+ *(unsigned short *)(&ptr[*idx + tlen ]) = HOST_TO_LE16(zcrc((uint8_t *)&ptr[*idx ], tlen));
+ *idx += tlen + 2;
+ return 0;
+}
+
+int binkv_add_field(char *buf, int buf_len, const char *field_name, const char *val)
+{
+ int dummy_idx = 0;
+ int i = 0;
+ int rv;
+
+ /* skip to the end, ignoring crc (don't want to further corrupt partially
+ * corrupt data) */
+ while(i < buf_len) {
+ uint8_t field_len = buf[i];
+
+ if(field_len == 0xff)
+ break;
+
+ /* skip past `field_len' bytes, 2 byte CRC field, and 1 byte len
+ * field */
+ i += field_len + 3;
+ }
+
+ rv = binkv_encode_field(buf + i, buf_len - i, &dummy_idx, field_name, val);
+ if(rv < 0)
+ return rv;
+
+ return 0;
+}
+