diff --git a/src/pyrite/lib/taxi b/src/pyrite/lib/taxi new file mode 120000 index 0000000..1b20c9f --- /dev/null +++ b/src/pyrite/lib/taxi @@ -0,0 +1 @@ +../../../ \ No newline at end of file diff --git a/src/pyrite/utils/Makefile b/src/pyrite/utils/Makefile new file mode 100644 index 0000000..bbdbab8 --- /dev/null +++ b/src/pyrite/utils/Makefile @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL +# Copyright (c) 2026 FPGA Ninja + +PREFIX ?= /usr/local +BINDIR = $(DESTDIR)$(PREFIX)/bin + +CC ?= gcc +CFLAGS ?= -O3 + +CFLAGS += -Wall +CPPFLAGS += +LDFLAGS += +LDLIBS += + +BIN = pyrite + +GENDEPFLAGS = -MD -MP -MF .$(@F).d + +ALL_CFLAGS = $(CFLAGS) $(CPPFLAGS) $(GENDEPFLAGS) + +all: $(BIN) + +%.o: %.c + $(CC) $(ALL_CFLAGS) -c -o $@ $< + +pyrite: pyrite.o flash.o flash_spi.o flash_bpi.o bitfile.o reg_block.o reg_if.o fpga_id.o + $(CC) $(ALL_CFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +install: + install -d $(BINDIR) + install -m 0755 $(BIN) $(BINDIR) + +clean: + rm -f $(BIN) + rm -f *.o + rm -f .*.d + +-include $(wildcard .*.d) + +.PHONY: all install clean diff --git a/src/pyrite/utils/bitfile.c b/src/pyrite/utils/bitfile.c new file mode 100644 index 0000000..99b2fc5 --- /dev/null +++ b/src/pyrite/utils/bitfile.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include +#include +#include + +#include "bitfile.h" + +struct bitfile *bitfile_create_from_file(const char *bit_file_name) +{ + struct bitfile *bf; + FILE *fp; + char *buffer; + char *data; + size_t len; + + fp = fopen(bit_file_name, "rb"); + + if (!fp) { + fprintf(stderr, "Failed to open file\n"); + return 0; + } + + fseek(fp, 0, SEEK_END); + len = ftell(fp); + rewind(fp); + + buffer = calloc(len + sizeof(struct bitfile), 1); + + if (!buffer) { + fprintf(stderr, "Failed to allocate memory\n"); + goto fail_file; + } + + bf = (struct bitfile *)buffer; + data = buffer + sizeof(struct bitfile); + + if (fread(data, 1, len, fp) < len) { + fprintf(stderr, "Error reading file\n"); + goto fail_buffer; + } + + fclose(fp); + + if (bitfile_parse(bf, data, len)) { + fprintf(stderr, "Failed to parse bitfile\n"); + goto fail_buffer; + } + + return bf; + +fail_buffer: + free(buffer); +fail_file: + fclose(fp); + return 0; +} + +struct bitfile *bitfile_create_from_buffer(char *buffer, size_t len) +{ + struct bitfile *bf; + + bf = calloc(1, sizeof(struct bitfile)); + + if (!bf) { + fprintf(stderr, "Failed to allocate memory\n"); + return 0; + } + + if (bitfile_parse(bf, buffer, len)) { + fprintf(stderr, "Failed to parse bitfile\n"); + free(bf); + return 0; + } + + return bf; +} + +int bitfile_parse(struct bitfile *bf, char *buffer, size_t len) +{ + char *ptr; + size_t l; + + ptr = buffer; + + bf->header = ptr; + + // drop unknown field + l = be16toh(*((uint16_t *)ptr)); + ptr += 2+l; + + // drop unknown field + ptr += 2; + + while (1) + { + int field_type = *ptr; + ptr += 1; + + if (field_type == 'e') { + l = be32toh(*((uint32_t *)ptr)); + bf->data_len = l; + bf->data = ptr+4; + return 0; + } else { + l = be16toh(*((uint16_t *)ptr)); + ptr += 2; + } + + switch (field_type) + { + case 'a': + bf->name = ptr; + break; + case 'b': + bf->part = ptr; + break; + case 'c': + bf->date = ptr; + break; + case 'd': + bf->time = ptr; + break; + default: + fprintf(stderr, "Unknown field type 0x%02x\n", field_type); + goto fail; + } + + ptr += l; + } + +fail: + return -1; +} + +void bitfile_close(struct bitfile *bf) +{ + if (bf) { + free(bf); + } +} diff --git a/src/pyrite/utils/bitfile.h b/src/pyrite/utils/bitfile.h new file mode 100644 index 0000000..738ee30 --- /dev/null +++ b/src/pyrite/utils/bitfile.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL */ +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#ifndef BITFILE_H +#define BITFILE_H + +#include + +struct bitfile { + char *header; + char *name; + char *part; + char *date; + char *time; + + size_t data_len; + char *data; +}; + +struct bitfile *bitfile_create_from_file(const char *bit_file_name); + +struct bitfile *bitfile_create_from_buffer(char *buffer, size_t len); + +int bitfile_parse(struct bitfile *bf, char *buffer, size_t len); + +void bitfile_close(struct bitfile *bf); + +#endif // BITFILE_H diff --git a/src/pyrite/utils/flash.c b/src/pyrite/utils/flash.c new file mode 100644 index 0000000..74b5cc6 --- /dev/null +++ b/src/pyrite/utils/flash.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include "flash.h" + +#include + +extern const struct flash_driver spi_flash_driver; +extern const struct flash_driver bpi_flash_driver; + +struct flash_device *flash_open_spi(int data_width, const struct reg_if *reg, size_t ctrl_reg_offset) +{ + struct flash_device *fdev; + + if (!reg) + return NULL; + + fdev = calloc(1, sizeof(struct flash_device)); + + if (!fdev) + return NULL; + + fdev->driver = &spi_flash_driver; + + fdev->data_width = data_width; + + fdev->reg = reg; + fdev->ctrl_reg_offset = ctrl_reg_offset; + + if (fdev->driver->init(fdev)) { + goto err; + } + + return fdev; + +err: + flash_release(fdev); + return NULL; +} + +struct flash_device *flash_open_bpi(int data_width, const struct reg_if *reg, size_t ctrl_reg_offset, size_t addr_reg_offset, size_t data_reg_offset) +{ + struct flash_device *fdev; + + if (!reg) + return NULL; + + fdev = calloc(1, sizeof(struct flash_device)); + + if (!fdev) + return NULL; + + fdev->driver = &bpi_flash_driver; + + fdev->data_width = data_width; + + fdev->reg = reg; + fdev->ctrl_reg_offset = ctrl_reg_offset; + fdev->addr_reg_offset = addr_reg_offset; + fdev->data_reg_offset = data_reg_offset; + + if (fdev->driver->init(fdev)) { + goto err; + } + + return fdev; + +err: + flash_release(fdev); + return NULL; +} + +void flash_release(struct flash_device *fdev) +{ + if (!fdev) + return; + + fdev->driver->release(fdev); + + free(fdev); +} + +int flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest) +{ + if (!fdev) + return -1; + + return fdev->driver->read(fdev, addr, len, dest); +} + +int flash_write(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + if (!fdev) + return -1; + + return fdev->driver->write(fdev, addr, len, src); +} + +int flash_erase(struct flash_device *fdev, size_t addr, size_t len) +{ + if (!fdev) + return -1; + + return fdev->driver->erase(fdev, addr, len); +} diff --git a/src/pyrite/utils/flash.h b/src/pyrite/utils/flash.h new file mode 100644 index 0000000..e4c3dc9 --- /dev/null +++ b/src/pyrite/utils/flash.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL */ +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#ifndef FLASH_H +#define FLASH_H + +#include +#include + +#include "reg_if.h" + +#define FLASH_ERASE_REGIONS 2 + +struct flash_driver; +struct flash_ops; + +struct flash_erase_region_info { + size_t block_count; + size_t block_size; + size_t region_start; + size_t region_end; +}; + +struct flash_device { + const struct flash_driver *driver; + const struct flash_ops *ops; + + const struct reg_if *reg; + + size_t ctrl_reg_offset; + size_t addr_reg_offset; + size_t data_reg_offset; + + size_t size; + int data_width; + + size_t write_buffer_size; + size_t erase_block_size; + + int protocol; + int bulk_protocol; + + int read_dummy_cycles; + + int erase_region_count; + struct flash_erase_region_info erase_region[FLASH_ERASE_REGIONS]; +}; + +struct flash_ops { + void (*init)(struct flash_device *fdev); + int (*sector_erase)(struct flash_device *fdev, size_t addr); + int (*buffered_program)(struct flash_device *fdev, size_t addr, size_t len, const void *src); +}; + +struct flash_driver { + int (*init)(struct flash_device *fdev); + void (*release)(struct flash_device *fdev); + int (*read)(struct flash_device *fdev, size_t addr, size_t len, void *dest); + int (*write)(struct flash_device *fdev, size_t addr, size_t len, const void *src); + int (*erase)(struct flash_device *fdev, size_t addr, size_t len); +}; + +struct flash_device *flash_open_spi(int data_width, const struct reg_if *reg, size_t ctrl_reg_offset); +struct flash_device *flash_open_bpi(int data_width, const struct reg_if *reg, size_t ctrl_reg_offset, size_t addr_reg_offset, size_t data_reg_offset); +void flash_release(struct flash_device *fdev); +int flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest); +int flash_write(struct flash_device *fdev, size_t addr, size_t len, const void *src); +int flash_erase(struct flash_device *fdev, size_t addr, size_t len); + +#endif /* FLASH_H */ diff --git a/src/pyrite/utils/flash_bpi.c b/src/pyrite/utils/flash_bpi.c new file mode 100644 index 0000000..89122e9 --- /dev/null +++ b/src/pyrite/utils/flash_bpi.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include "flash.h" + +#include +#include + +#define reg_read32(reg) (*((volatile uint32_t *)(reg))) +#define reg_write32(reg, val) (*((volatile uint32_t *)(reg))) = (val) + +#define CFI_QUERY_ADDR 0x55 +#define CFI_QUERY_DATA 0x98 +#define CFI_READ_ARRAY 0xFF +#define CFI_READ_ARRAY_ALT 0xF0 +#define CFI_ID_0 0x10 +#define CFI_ID_1 0x11 +#define CFI_ID_2 0x12 +#define CFI_PRI_CMD_SET_0 0x13 +#define CFI_PRI_CMD_SET_1 0x14 +#define CFI_DEVICE_SIZE 0x27 +#define CFI_WRITE_BUFFER_SIZE_0 0x2A +#define CFI_WRITE_BUFFER_SIZE_1 0x2B +#define CFI_ERASE_REGION_COUNT 0x2C +#define CFI_ERASE_REGION_1_INFO_0 0x2D +#define CFI_ERASE_REGION_1_INFO_1 0x2E +#define CFI_ERASE_REGION_1_INFO_2 0x2F +#define CFI_ERASE_REGION_1_INFO_3 0x30 +#define CFI_ERASE_REGION_2_INFO_0 0x31 +#define CFI_ERASE_REGION_2_INFO_1 0x32 +#define CFI_ERASE_REGION_2_INFO_2 0x33 +#define CFI_ERASE_REGION_2_INFO_3 0x34 + +#define BPI_INTEL_READ_ARRAY 0xFF +#define BPI_INTEL_READ_STATUS_REG 0x70 +#define BPI_INTEL_READ_ID 0x90 +#define BPI_INTEL_CLEAR_STATUS_REG 0x50 +#define BPI_INTEL_READ_CONFIG_REG_SETUP 0x60 +#define BPI_INTEL_SET_READ_CONFIG_REG 0x03 +#define BPI_INTEL_BLOCK_LOCK_SETUP 0x60 +#define BPI_INTEL_BLOCK_LOCK 0x01 +#define BPI_INTEL_BLOCK_UNLOCK 0xD0 +#define BPI_INTEL_BLOCK_ERASE_SETUP 0x20 +#define BPI_INTEL_BLOCK_ERASE_CONFIRM 0xD0 +#define BPI_INTEL_BUFFERED_PROGRAM_SETUP 0xE8 +#define BPI_INTEL_BUFFERED_PROGRAM_CONFIRM 0xD0 + +#define BPI_AMD_UNLOCK_ADDR_1 0x555 +#define BPI_AMD_UNLOCK_DATA_1 0xAA +#define BPI_AMD_UNLOCK_ADDR_2 0x2AA +#define BPI_AMD_UNLOCK_DATA_2 0x55 +#define BPI_AMD_UNLOCK_BYPASS_ENTER_ADDR 0x555 +#define BPI_AMD_UNLOCK_BYPASS_ENTER_DATA 0x20 +#define BPI_AMD_UNLOCK_BYPASS_RESET_1 0x90 +#define BPI_AMD_UNLOCK_BYPASS_RESET_2 0x00 +#define BPI_AMD_BLOCK_ERASE_SETUP 0x80 +#define BPI_AMD_BLOCK_ERASE_CONFIRM 0x30 +#define BPI_AMD_BUFFERED_PROGRAM_SETUP 0x25 +#define BPI_AMD_BUFFERED_PROGRAM_CONFIRM 0x29 +#define BPI_AMD_READ_ARRAY 0xF0 + +#define BPI_MICRON_READ_ARRAY 0xFF +#define BPI_MICRON_READ_STATUS_REG 0x70 +#define BPI_MICRON_READ_ID 0x90 +#define BPI_MICRON_CLEAR_STATUS_REG 0x50 +#define BPI_MICRON_READ_CONFIG_REG_SETUP 0x60 +#define BPI_MICRON_SET_READ_CONFIG_REG 0x03 +#define BPI_MICRON_BLOCK_LOCK_SETUP 0x60 +#define BPI_MICRON_BLOCK_LOCK 0x01 +#define BPI_MICRON_BLOCK_UNLOCK 0xD0 +#define BPI_MICRON_BLOCK_ERASE_SETUP 0x20 +#define BPI_MICRON_BLOCK_ERASE_CONFIRM 0xD0 +#define BPI_MICRON_BUFFERED_PROGRAM_SETUP 0xE9 +#define BPI_MICRON_BUFFERED_PROGRAM_CONFIRM 0xD0 + +#define FLASH_CE_N (1 << 0) +#define FLASH_OE_N (1 << 1) +#define FLASH_WE_N (1 << 2) +#define FLASH_ADV_N (1 << 3) +#define FLASH_DQ_OE (1 << 8) +#define FLASH_REGION_OE (1 << 16) + +void bpi_flash_set_addr(struct flash_device *fdev, size_t addr) +{ + reg_if_write32(fdev->reg, fdev->addr_reg_offset, addr); +} + +uint16_t bpi_flash_read_cur(struct flash_device *fdev) +{ + uint32_t val; + + reg_if_write32(fdev->reg, fdev->ctrl_reg_offset, FLASH_REGION_OE | FLASH_WE_N); + reg_if_read32(fdev->reg, fdev->ctrl_reg_offset, &val); // dummy read + reg_if_read32(fdev->reg, fdev->data_reg_offset, &val); + reg_if_write32(fdev->reg, fdev->ctrl_reg_offset, FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N); + + return val; +} + +uint16_t bpi_flash_read_word(struct flash_device *fdev, size_t addr) +{ + bpi_flash_set_addr(fdev, addr); + return bpi_flash_read_cur(fdev); +} + +void bpi_flash_write_cur(struct flash_device *fdev, uint16_t data) +{ + uint32_t val; + + reg_if_write32(fdev->reg, fdev->data_reg_offset, data); + reg_if_write32(fdev->reg, fdev->ctrl_reg_offset, FLASH_REGION_OE | FLASH_DQ_OE | FLASH_OE_N); + reg_if_read32(fdev->reg, fdev->ctrl_reg_offset, &val); // dummy read + reg_if_write32(fdev->reg, fdev->ctrl_reg_offset, FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N); +} + +void bpi_flash_write_word(struct flash_device *fdev, size_t addr, uint16_t data) +{ + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, data); +} + +void bpi_flash_deselect(struct flash_device *fdev) +{ + bpi_flash_write_word(fdev, 0, CFI_READ_ARRAY); + reg_if_write32(fdev->reg, fdev->ctrl_reg_offset, FLASH_CE_N | FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N); +} + +// Intel flash ops (0x0001) + +uint16_t bpi_flash_intel_read_status_register(struct flash_device *fdev) +{ + bpi_flash_write_cur(fdev, BPI_INTEL_READ_STATUS_REG); + return bpi_flash_read_cur(fdev); +} + +void bpi_flash_intel_clear_status_register(struct flash_device *fdev) +{ + bpi_flash_write_cur(fdev, BPI_INTEL_CLEAR_STATUS_REG); +} + +void bpi_flash_intel_init(struct flash_device *fdev) +{ + bpi_flash_intel_clear_status_register(fdev); +} + +int bpi_flash_intel_sector_erase(struct flash_device *fdev, size_t addr) +{ + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_LOCK_SETUP); + bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_UNLOCK); + + if (bpi_flash_intel_read_status_register(fdev) & 0x30) { + fprintf(stderr, "Failed to unlock block\n"); + return -1; + } + + bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_ERASE_SETUP); + bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_ERASE_CONFIRM); + + while (!(bpi_flash_intel_read_status_register(fdev) & 0x80)) {}; + + if (bpi_flash_intel_read_status_register(fdev) & 0x30){ + fprintf(stderr, "Failed to erase block\n"); + return -1; + } + + return 0; +} + +int bpi_flash_intel_buffered_program(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + const uint8_t *s = (const uint8_t *)src; + + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_INTEL_BUFFERED_PROGRAM_SETUP); + bpi_flash_write_cur(fdev, len-1); + + for (size_t i = 0; i < len; i++) { + bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8)); + s += 2; + } + + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_INTEL_BUFFERED_PROGRAM_CONFIRM); + + while (!(bpi_flash_intel_read_status_register(fdev) & 0x80)) {}; + + if (bpi_flash_intel_read_status_register(fdev) & 0x30) { + fprintf(stderr, "Failed to write block\n"); + return -1; + } + + return 0; +} + +const struct flash_ops bpi_flash_intel_ops = { + .init = bpi_flash_intel_init, + .sector_erase = bpi_flash_intel_sector_erase, + .buffered_program = bpi_flash_intel_buffered_program +}; + +// AMD flash ops (0x0002) + +void bpi_flash_amd_unlock(struct flash_device *fdev) +{ + bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_UNLOCK_DATA_1); + bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_2, BPI_AMD_UNLOCK_DATA_2); +} + +void bpi_flash_amd_write_buffer_abort_reset(struct flash_device *fdev) +{ + bpi_flash_amd_unlock(fdev); + bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_READ_ARRAY); +} + +void bpi_flash_amd_init(struct flash_device *fdev) +{ + // write-to-buffer-abort reset (just in case) + bpi_flash_amd_write_buffer_abort_reset(fdev); +} + +int bpi_flash_amd_wait_for_operation(struct flash_device *fdev, uint16_t stop_mask) +{ + uint16_t read_1, read_2, read_3; + + while (1) { + read_1 = bpi_flash_read_cur(fdev); + read_2 = bpi_flash_read_cur(fdev); + read_3 = bpi_flash_read_cur(fdev); + + if ((read_1 ^ read_2) & (read_2 ^ read_3) & 0x40) { + if (read_1 & stop_mask) { + return read_1; + } + } else { + return 0; + } + } +} + +int bpi_flash_amd_sector_erase(struct flash_device *fdev, size_t addr) +{ + bpi_flash_amd_unlock(fdev); + bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_BLOCK_ERASE_SETUP); + bpi_flash_amd_unlock(fdev); + bpi_flash_write_word(fdev, addr, BPI_AMD_BLOCK_ERASE_CONFIRM); + + while (!(bpi_flash_read_cur(fdev) & 0x08)) {}; + + if (bpi_flash_amd_wait_for_operation(fdev, 0x20) & 0x20) { + // write-to-buffer-abort reset + bpi_flash_amd_write_buffer_abort_reset(fdev); + + fprintf(stderr, "Failed to erase block\n"); + return -1; + } + + return 0; +} + +int bpi_flash_amd_buffered_program(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + const uint8_t *s = (const uint8_t *)src; + + bpi_flash_amd_unlock(fdev); + bpi_flash_write_word(fdev, addr, BPI_AMD_BUFFERED_PROGRAM_SETUP); + bpi_flash_write_cur(fdev, len-1); + + for (size_t i = 0; i < len; i++) { + bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8)); + s += 2; + } + + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_AMD_BUFFERED_PROGRAM_CONFIRM); + + if (bpi_flash_amd_wait_for_operation(fdev, 0x22) & 0x22) { + // write-to-buffer-abort reset + bpi_flash_amd_write_buffer_abort_reset(fdev); + + fprintf(stderr, "Failed to write block\n"); + return -1; + } + + return 0; +} + +const struct flash_ops bpi_flash_amd_ops = { + .init = bpi_flash_amd_init, + .sector_erase = bpi_flash_amd_sector_erase, + .buffered_program = bpi_flash_amd_buffered_program +}; + +// Micron flash ops (0x0002) + +uint16_t bpi_flash_micron_read_status_register(struct flash_device *fdev) +{ + bpi_flash_write_cur(fdev, BPI_MICRON_READ_STATUS_REG); + return bpi_flash_read_cur(fdev); +} + +void bpi_flash_micron_clear_status_register(struct flash_device *fdev) +{ + bpi_flash_write_cur(fdev, BPI_MICRON_CLEAR_STATUS_REG); +} + +void bpi_flash_micron_init(struct flash_device *fdev) +{ + bpi_flash_micron_clear_status_register(fdev); +} + +int bpi_flash_micron_sector_erase(struct flash_device *fdev, size_t addr) +{ + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_LOCK_SETUP); + bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_UNLOCK); + + if (bpi_flash_micron_read_status_register(fdev) & 0x30) { + fprintf(stderr, "Failed to unlock block\n"); + bpi_flash_write_cur(fdev, CFI_READ_ARRAY); + return -1; + } + + bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_ERASE_SETUP); + bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_ERASE_CONFIRM); + + while (!(bpi_flash_micron_read_status_register(fdev) & 0x80)) {}; + + if (bpi_flash_micron_read_status_register(fdev) & 0x30) { + fprintf(stderr, "Failed to erase block\n"); + bpi_flash_write_cur(fdev, CFI_READ_ARRAY); + return -1; + } + + bpi_flash_write_cur(fdev, CFI_READ_ARRAY); + + return 0; +} + +int bpi_flash_micron_buffered_program(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + const uint8_t *s = (const uint8_t *)src; + + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_MICRON_BUFFERED_PROGRAM_SETUP); + bpi_flash_write_cur(fdev, len-1); + + for (size_t i = 0; i < len; i++) { + bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8)); + s += 2; + } + + bpi_flash_set_addr(fdev, addr); + bpi_flash_write_cur(fdev, BPI_MICRON_BUFFERED_PROGRAM_CONFIRM); + + while (!(bpi_flash_micron_read_status_register(fdev) & 0x80)) {}; + + if (bpi_flash_micron_read_status_register(fdev) & 0x30) { + fprintf(stderr, "Failed to write block\n"); + bpi_flash_write_cur(fdev, CFI_READ_ARRAY); + return -1; + } + + bpi_flash_write_cur(fdev, CFI_READ_ARRAY); + + return 0; +} + +const struct flash_ops bpi_flash_micron_ops = { + .init = bpi_flash_micron_init, + .sector_erase = bpi_flash_micron_sector_erase, + .buffered_program = bpi_flash_micron_buffered_program +}; + + +void bpi_flash_release(struct flash_device *fdev) +{ + bpi_flash_deselect(fdev); +} + +int bpi_flash_init(struct flash_device *fdev) +{ + int ret = 0; + + if (!fdev) + return -1; + + // CFI query + bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA); + + if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q') { + // may be Intel flash in sync read mode; attempt switch to async + bpi_flash_write_word(fdev, 0xf94f, BPI_INTEL_READ_CONFIG_REG_SETUP); + bpi_flash_write_word(fdev, 0xf94f, BPI_INTEL_SET_READ_CONFIG_REG); + bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA); + } + + if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q' && ((bpi_flash_read_cur(fdev) ^ bpi_flash_read_cur(fdev)) & 0x44)) { + // may be AMD flash in write buffer abort; perform write buffer abort reset + bpi_flash_amd_write_buffer_abort_reset(fdev); + bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA); + } + + if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q' || + bpi_flash_read_word(fdev, CFI_ID_1) != 'R' || + bpi_flash_read_word(fdev, CFI_ID_2) != 'Y') { + fprintf(stderr, "Failed to read flash ID\n"); + ret = -1; + goto err; + } + + fdev->protocol = bpi_flash_read_word(fdev, CFI_PRI_CMD_SET_0) | (bpi_flash_read_word(fdev, CFI_PRI_CMD_SET_1) << 8); + + printf("Command set: %d\n", fdev->protocol); + + switch (fdev->protocol) { + case 0x0001: + // Intel command set (P30) + fdev->ops = &bpi_flash_intel_ops; + break; + case 0x0002: + // AMD command set (S29, MT28) + fdev->ops = &bpi_flash_amd_ops; + break; + case 0x0200: + // Micron + fdev->ops = &bpi_flash_micron_ops; + break; + default: + fprintf(stderr, "Unknown command set: %d\n", fdev->protocol); + ret = -1; + goto err; + } + + uint8_t flash_size = bpi_flash_read_word(fdev, CFI_DEVICE_SIZE); + fdev->size = ((size_t)1) << flash_size; + + printf("Flash size: %d MB\n", 1 << (flash_size-20)); + + uint16_t write_buffer_size = bpi_flash_read_word(fdev, CFI_WRITE_BUFFER_SIZE_0) | (bpi_flash_read_word(fdev, CFI_WRITE_BUFFER_SIZE_1) << 8); + fdev->write_buffer_size = ((size_t)1) << write_buffer_size; + + printf("Write buffer size: %ld B\n", fdev->write_buffer_size); + + fdev->erase_region_count = bpi_flash_read_word(fdev, CFI_ERASE_REGION_COUNT); + + printf("Erase regions: %d\n", fdev->erase_region_count); + + if (fdev->erase_region_count > 0) { + fdev->erase_region[0].block_count = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_0) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_1) << 8)) + 1; + fdev->erase_region[0].block_size = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_2) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_3) << 8)) * 256; + fdev->erase_region[0].region_start = 0; + fdev->erase_region[0].region_end = fdev->erase_region[0].region_start + fdev->erase_region[0].block_count * fdev->erase_region[0].block_size; + + fdev->erase_block_size = fdev->erase_region[0].block_size; + + printf("Erase region 0 block count: %ld\n", fdev->erase_region[0].block_count); + printf("Erase region 0 block size: %ld B\n", fdev->erase_region[0].block_size); + printf("Erase region 0 start: 0x%08lx\n", fdev->erase_region[0].region_start); + printf("Erase region 0 end: 0x%08lx\n", fdev->erase_region[0].region_end); + } else { + fprintf(stderr, "No erase regions found!\n"); + ret = -1; + goto err; + } + + if (fdev->erase_region_count > 1) { + fdev->erase_region[1].block_count = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_0) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_1) << 8)) + 1; + fdev->erase_region[1].block_size = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_2) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_3) << 8)) * 256; + fdev->erase_region[1].region_start = fdev->erase_region[0].region_end; + fdev->erase_region[1].region_end = fdev->erase_region[1].region_start + fdev->erase_region[1].block_count * fdev->erase_region[1].block_size; + + if (fdev->erase_region[1].block_size > fdev->erase_block_size) { + fdev->erase_block_size = fdev->erase_region[1].block_size; + } + + printf("Erase region 1 block count: %ld\n", fdev->erase_region[1].block_count); + printf("Erase region 1 block size: %ld B\n", fdev->erase_region[1].block_size); + printf("Erase region 1 start: 0x%08lx\n", fdev->erase_region[1].region_start); + printf("Erase region 1 end: 0x%08lx\n", fdev->erase_region[1].region_end); + } + + printf("Erase block size: %ld B\n", fdev->erase_block_size); + + fdev->ops->init(fdev); + +err: + bpi_flash_release(fdev); + return ret; +} + +int bpi_flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest) +{ + char *d = dest; + + bpi_flash_write_word(fdev, 0, CFI_READ_ARRAY); + + if (addr & 1) { + *d = bpi_flash_read_word(fdev, addr >> 1) >> 8; + addr++; + len--; + d++; + } + + while (len > 1) { + *((uint16_t *)d) = bpi_flash_read_word(fdev, addr >> 1); + addr += 2; + len -= 2; + d += 2; + } + + if (len) { + *d = bpi_flash_read_word(fdev, addr >> 1); + addr++; + len--; + d++; + } + + bpi_flash_deselect(fdev); + + return 0; +} + +int bpi_flash_write(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + const char *s = src; + + while (len > 0) { + size_t seg = len; + + // align to buffer size + if (seg > fdev->write_buffer_size - (addr & (fdev->write_buffer_size-1))) { + seg = fdev->write_buffer_size - (addr & (fdev->write_buffer_size-1)); + } + + if (fdev->ops->buffered_program(fdev, addr >> 1, seg >> 1, s)) { + fprintf(stderr, "Buffered write failed\n"); + bpi_flash_deselect(fdev); + return -1; + } + + addr += seg; + len -= seg; + s += seg; + } + + bpi_flash_deselect(fdev); + + return 0; +} + +int bpi_flash_erase(struct flash_device *fdev, size_t addr, size_t len) +{ + size_t erase_block_size = fdev->erase_block_size; + + while (len > 0) { + // determine sector size + erase_block_size = 0; + + for (int k = 0; k < fdev->erase_region_count; k++) { + if (addr >= fdev->erase_region[k].region_start && addr < fdev->erase_region[k].region_end) { + erase_block_size = fdev->erase_region[k].block_size; + break; + } + } + + if (!erase_block_size) { + fprintf(stderr, "Address does not match an erase region\n"); + bpi_flash_deselect(fdev); + return -1; + } + + // check size and alignment + if (addr & (erase_block_size-1) || len < erase_block_size) { + fprintf(stderr, "Invalid erase request\n"); + bpi_flash_deselect(fdev); + return -1; + } + + // block erase + if (fdev->ops->sector_erase(fdev, addr >> 1)) { + fprintf(stderr, "Failed to erase sector\n"); + bpi_flash_deselect(fdev); + return -1; + } + + if (len <= erase_block_size) + break; + + addr += erase_block_size; + len -= erase_block_size; + } + + bpi_flash_deselect(fdev); + + return 0; +} + +const struct flash_driver bpi_flash_driver = { + .init = bpi_flash_init, + .release = bpi_flash_release, + .read = bpi_flash_read, + .write = bpi_flash_write, + .erase = bpi_flash_erase +}; diff --git a/src/pyrite/utils/flash_spi.c b/src/pyrite/utils/flash_spi.c new file mode 100644 index 0000000..2322992 --- /dev/null +++ b/src/pyrite/utils/flash_spi.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include "flash.h" + +#include +#include + +#define SPI_CMD_RESET_ENABLE 0x66 +#define SPI_CMD_RESET_MEMORY 0x99 +#define SPI_CMD_READ_ID 0x9F +#define SPI_CMD_READ 0x03 +#define SPI_CMD_FAST_READ 0x0B +#define SPI_CMD_FAST_READ_DUAL_OUT 0x3B +#define SPI_CMD_FAST_READ_DUAL_IO 0xBB +#define SPI_CMD_FAST_READ_QUAD_OUT 0x6B +#define SPI_CMD_FAST_READ_QUAD_IO 0xEB +#define SPI_CMD_DTR_FAST_READ 0x0D +#define SPI_CMD_DTR_FAST_READ_DUAL_OUT 0x3D +#define SPI_CMD_DTR_FAST_READ_DUAL_IO 0xBD +#define SPI_CMD_DTR_FAST_READ_QUAD_OUT 0x6D +#define SPI_CMD_DTR_FAST_READ_QUAD_IO 0xED +#define SPI_CMD_4B_READ 0x13 +#define SPI_CMD_4B_FAST_READ 0x0C +#define SPI_CMD_4B_FAST_READ_DUAL_OUT 0x3C +#define SPI_CMD_4B_FAST_READ_DUAL_IO 0xBC +#define SPI_CMD_4B_FAST_READ_QUAD_OUT 0x6C +#define SPI_CMD_4B_FAST_READ_QUAD_IO 0xEC +#define SPI_CMD_4B_DTR_FAST_READ 0x0E +#define SPI_CMD_4B_DTR_FAST_READ_DUAL_IO 0xBE +#define SPI_CMD_4B_DTR_FAST_READ_QUAD_IO 0xEE +#define SPI_CMD_WRITE_ENABLE 0x06 +#define SPI_CMD_WRITE_DISABLE 0x04 +#define SPI_CMD_READ_STATUS_REG 0x05 +#define SPI_CMD_READ_FLAG_STATUS_REG 0x70 +#define SPI_CMD_READ_NV_CONFIG_REG 0xB5 +#define SPI_CMD_READ_V_CONFIG_REG 0x85 +#define SPI_CMD_READ_EV_CONFIG_REG 0x65 +#define SPI_CMD_READ_EXT_ADDR_REG 0xC8 +#define SPI_CMD_WRITE_STATUS_REG 0x01 +#define SPI_CMD_WRITE_NV_CONFIG_REG 0xB1 +#define SPI_CMD_WRITE_V_CONFIG_REG 0x81 +#define SPI_CMD_WRITE_EV_CONFIG_REG 0x61 +#define SPI_CMD_WRITE_EXT_ADDR_REG 0xC5 +#define SPI_CMD_CLEAR_FLAG_STATUS_REG 0x50 +#define SPI_CMD_PAGE_PROGRAM 0x02 +#define SPI_CMD_PAGE_PROGRAM_DUAL_IN 0xA2 +#define SPI_CMD_PAGE_PROGRAM_DUAL_IN_EXT 0xD2 +#define SPI_CMD_PAGE_PROGRAM_QUAD_IN 0x32 +#define SPI_CMD_PAGE_PROGRAM_QUAD_IN_EXT 0x38 +#define SPI_CMD_4B_PAGE_PROGRAM 0x12 +#define SPI_CMD_4B_PAGE_PROGRAM_QUAD_IN 0x34 +#define SPI_CMD_4B_PAGE_PROGRAM_QUAD_IN_EXT 0x3E +#define SPI_CMD_32KB_SUBSECTOR_ERASE 0x52 +#define SPI_CMD_4KB_SUBSECTOR_ERASE 0x20 +#define SPI_CMD_SECTOR_ERASE 0xD8 +#define SPI_CMD_BULK_ERASE 0xC7 +#define SPI_CMD_4B_4KB_SUBSECTOR_ERASE 0x21 +#define SPI_CMD_4B_SECTOR_ERASE 0xDC +#define SPI_CMD_PROGRAM_SUSPEND 0x75 +#define SPI_CMD_PROGRAM_RESUME 0x7A +#define SPI_CMD_READ_OTP_ARRAY 0x4B +#define SPI_CMD_PROGRAM_OTP_ARRAY 0x42 +#define SPI_CMD_ENTER_4B_ADDR_MODE 0xB7 +#define SPI_CMD_EXIT_4B_ADDR_MODE 0xE9 +#define SPI_CMD_ENTER_QUAD_IO_MODE 0x35 +#define SPI_CMD_EXIT_QUAD_IO_MODE 0xF5 +#define SPI_CMD_ENTER_DEEP_POWER_DOWN 0xB9 +#define SPI_CMD_EXIT_DEEP_POWER_DOWN 0xAB +#define SPI_CMD_READ_SECTOR_PROTECTION 0x2D +#define SPI_CMD_PRGM_SECTOR_PROTECTION 0x2C +#define SPI_CMD_READ_V_LOCK_BITS 0xE8 +#define SPI_CMD_WRITE_V_LOCK_BITS 0xE5 +#define SPI_CMD_4B_READ_V_LOCK_BITS 0xE0 +#define SPI_CMD_4B_WRITE_V_LOCK_BITS 0xE1 +#define SPI_CMD_READ_NV_LOCK_BITS 0xE2 +#define SPI_CMD_PRGM_NV_LOCK_BITS 0xE3 +#define SPI_CMD_ERASE_NV_LOCK_BITS 0xE4 +#define SPI_CMD_READ_GLOBAL_FREEZE_BIT 0xA7 +#define SPI_CMD_WRITE_GLOBAL_FREEZE_BIT 0xA6 +#define SPI_CMD_READ_PASSWORD 0x27 +#define SPI_CMD_WRITE_PASSWORD 0x28 +#define SPI_CMD_UNLOCK_PASSWORD 0x29 + +// Macronix +#define SPI_MXIC_CMD_RDCR 0x15 +#define SPI_MXIC_CMD_RDSCUR 0x2B +#define SPI_MXIC_CMD_WRSCUR 0x2F +#define SPI_MXIC_CMD_GBLK 0x7E +#define SPI_MXIC_CMD_GBULK 0x98 +#define SPI_MXIC_CMD_WRLR 0x2C +#define SPI_MXIC_CMD_RDLR 0x2D +#define SPI_MXIC_CMD_WRSPB 0xE3 +#define SPI_MXIC_CMD_ESSPB 0xE4 +#define SPI_MXIC_CMD_RDSPB 0xE2 +#define SPI_MXIC_CMD_WRDPB 0xE1 +#define SPI_MXIC_CMD_RDDPB 0xE0 + +#define SPI_PROTO_STR 0 +#define SPI_PROTO_DTR 1 +#define SPI_PROTO_DUAL_STR 2 +#define SPI_PROTO_DUAL_DTR 3 +#define SPI_PROTO_QUAD_STR 4 +#define SPI_PROTO_QUAD_DTR 5 + +#define SPI_PAGE_SIZE 0x100 +#define SPI_SUBSECTOR_SIZE 0x1000 +#define SPI_SECTOR_SIZE 0x10000 + +#define FLASH_D_0 (1 << 0) +#define FLASH_D_1 (1 << 1) +#define FLASH_D_2 (1 << 2) +#define FLASH_D_3 (1 << 3) +#define FLASH_D_01 (FLASH_D_0 | FLASH_D_1) +#define FLASH_D_0123 (FLASH_D_0 | FLASH_D_1 | FLASH_D_2 | FLASH_D_3) +#define FLASH_OE_0 (1 << 8) +#define FLASH_OE_1 (1 << 9) +#define FLASH_OE_2 (1 << 10) +#define FLASH_OE_3 (1 << 11) +#define FLASH_OE_01 (FLASH_OE_0 | FLASH_OE_1) +#define FLASH_OE_0123 (FLASH_OE_0 | FLASH_OE_1 | FLASH_OE_2 | FLASH_OE_3) +#define FLASH_CLK (1 << 16) +#define FLASH_CS_N (1 << 17) + +static uint32_t ctrl_reg_read(struct flash_device *fdev) +{ + uint32_t reg_val = 0; + reg_if_read32(fdev->reg, fdev->ctrl_reg_offset, ®_val); + return reg_val; +} + +static void ctrl_reg_write(struct flash_device *fdev, uint32_t val) +{ + reg_if_write32(fdev->reg, fdev->ctrl_reg_offset, val); +} + +void spi_flash_select(struct flash_device *fdev) +{ + ctrl_reg_write(fdev, 0); +} + +void spi_flash_deselect(struct flash_device *fdev) +{ + ctrl_reg_write(fdev, FLASH_CS_N); +} + +uint8_t spi_flash_read_byte(struct flash_device *fdev, int protocol) +{ + uint8_t val = 0; + + switch (protocol){ + case SPI_PROTO_STR: + for (int i = 7; i >= 0; i--) { + ctrl_reg_write(fdev, 0); + ctrl_reg_read(fdev); // dummy read + val |= ((ctrl_reg_read(fdev) & FLASH_D_1) != 0) << i; + ctrl_reg_write(fdev, FLASH_CLK); + ctrl_reg_read(fdev); // dummy read + } + break; + case SPI_PROTO_DTR: + break; + case SPI_PROTO_DUAL_STR: + for (int i = 6; i >= 0; i -= 2) { + ctrl_reg_write(fdev, 0); + ctrl_reg_read(fdev); // dummy read + val |= (ctrl_reg_read(fdev) & FLASH_D_01) << i; + ctrl_reg_write(fdev, FLASH_CLK); + ctrl_reg_read(fdev); // dummy read + } + break; + case SPI_PROTO_DUAL_DTR: + break; + case SPI_PROTO_QUAD_STR: + for (int i = 4; i >= 0; i -= 4) { + ctrl_reg_write(fdev, 0); + ctrl_reg_read(fdev); // dummy read + val |= (ctrl_reg_read(fdev) & FLASH_D_0123) << i; + ctrl_reg_write(fdev, FLASH_CLK); + ctrl_reg_read(fdev); // dummy read + } + break; + case SPI_PROTO_QUAD_DTR: + break; + } + + ctrl_reg_write(fdev, 0); + + return val; +} + +void spi_flash_write_byte(struct flash_device *fdev, uint8_t val, int protocol) +{ + uint8_t bit; + + switch (protocol){ + case SPI_PROTO_STR: + for (int i = 7; i >= 0; i--) { + bit = (val >> i) & 0x1; + ctrl_reg_write(fdev, bit | FLASH_OE_0); + ctrl_reg_read(fdev); // dummy read + ctrl_reg_write(fdev, bit | FLASH_OE_0 | FLASH_CLK); + ctrl_reg_read(fdev); // dummy read + } + break; + case SPI_PROTO_DTR: + break; + case SPI_PROTO_DUAL_STR: + for (int i = 6; i >= 0; i -= 2) { + bit = (val >> i) & 0x3; + ctrl_reg_write(fdev, bit | FLASH_OE_01); + ctrl_reg_read(fdev); // dummy read + ctrl_reg_write(fdev, bit | FLASH_OE_01 | FLASH_CLK); + ctrl_reg_read(fdev); // dummy read + } + break; + case SPI_PROTO_DUAL_DTR: + break; + case SPI_PROTO_QUAD_STR: + for (int i = 4; i >= 0; i -= 4) { + bit = (val >> i) & 0xf; + ctrl_reg_write(fdev, bit | FLASH_OE_0123); + ctrl_reg_read(fdev); // dummy read + ctrl_reg_write(fdev, bit | FLASH_OE_0123 | FLASH_CLK); + ctrl_reg_read(fdev); // dummy read + } + break; + case SPI_PROTO_QUAD_DTR: + break; + } + + ctrl_reg_write(fdev, 0); +} + +void spi_flash_write_addr(struct flash_device *fdev, size_t addr, int protocol) +{ + spi_flash_write_byte(fdev, (addr >> 16) & 0xff, protocol); + spi_flash_write_byte(fdev, (addr >> 8) & 0xff, protocol); + spi_flash_write_byte(fdev, (addr >> 0) & 0xff, protocol); +} + +void spi_flash_write_addr_4b(struct flash_device *fdev, size_t addr, int protocol) +{ + spi_flash_write_byte(fdev, (addr >> 24) & 0xff, protocol); + spi_flash_write_byte(fdev, (addr >> 16) & 0xff, protocol); + spi_flash_write_byte(fdev, (addr >> 8) & 0xff, protocol); + spi_flash_write_byte(fdev, (addr >> 0) & 0xff, protocol); +} + +void spi_flash_write_enable(struct flash_device *fdev, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_WRITE_ENABLE, protocol); + spi_flash_deselect(fdev); +} + +void spi_flash_write_disable(struct flash_device *fdev, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_WRITE_DISABLE, protocol); + spi_flash_deselect(fdev); +} + +uint8_t spi_flash_read_status_reg(struct flash_device *fdev, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_CMD_READ_STATUS_REG, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +void spi_flash_write_status_reg(struct flash_device *fdev, uint8_t val, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_WRITE_STATUS_REG, protocol); + spi_flash_write_byte(fdev, val, protocol); + spi_flash_deselect(fdev); +} + +void spi_mxic_flash_write_status_cfg_reg(struct flash_device *fdev, uint8_t status, uint8_t cfg, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_WRITE_STATUS_REG, protocol); + spi_flash_write_byte(fdev, status, protocol); + spi_flash_write_byte(fdev, cfg, protocol); + spi_flash_deselect(fdev); +} + +uint8_t spi_mxic_flash_read_cfg_reg(struct flash_device *fdev, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_MXIC_CMD_RDCR, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +uint8_t spi_mxic_flash_read_security_reg(struct flash_device *fdev, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_MXIC_CMD_RDSCUR, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +uint8_t spi_flash_read_flag_status_reg(struct flash_device *fdev, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_CMD_READ_FLAG_STATUS_REG, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +void spi_flash_clear_flag_status_reg(struct flash_device *fdev, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_CLEAR_FLAG_STATUS_REG, protocol); + spi_flash_deselect(fdev); +} + +uint8_t spi_flash_read_volatile_cfg_reg(struct flash_device *fdev, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_CMD_READ_V_CONFIG_REG, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +void spi_flash_write_volatile_config_reg(struct flash_device *fdev, uint8_t val, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_WRITE_V_CONFIG_REG, protocol); + spi_flash_write_byte(fdev, val, protocol); + spi_flash_deselect(fdev); +} + +uint16_t spi_flash_read_sector_protection_reg(struct flash_device *fdev, int protocol) +{ + uint16_t val; + spi_flash_write_byte(fdev, SPI_CMD_READ_SECTOR_PROTECTION, protocol); + val = spi_flash_read_byte(fdev, protocol); + val |= (uint16_t)spi_flash_read_byte(fdev, protocol) << 8; + spi_flash_deselect(fdev); + return val; +} + +uint8_t spi_flash_read_global_freeze_bit(struct flash_device *fdev, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_CMD_READ_GLOBAL_FREEZE_BIT, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +uint8_t spi_flash_read_nv_lock_bits(struct flash_device *fdev, size_t addr, int protocol) +{ + uint8_t val; + spi_flash_write_byte(fdev, SPI_CMD_READ_NV_LOCK_BITS, protocol); + spi_flash_write_addr_4b(fdev, addr, protocol); + val = spi_flash_read_byte(fdev, protocol); + spi_flash_deselect(fdev); + return val; +} + +void spi_flash_unlock_password(struct flash_device *fdev, char *val, int protocol) +{ + spi_flash_write_byte(fdev, SPI_CMD_UNLOCK_PASSWORD, protocol); + for (int k = 0; k < 8; k++) + spi_flash_write_byte(fdev, val[k], protocol); + spi_flash_deselect(fdev); +} + +void spi_flash_reset(struct flash_device *fdev, int protocol) +{ + spi_flash_deselect(fdev); + spi_flash_write_byte(fdev, SPI_CMD_RESET_ENABLE, protocol); + spi_flash_deselect(fdev); + ctrl_reg_read(fdev); // dummy read + ctrl_reg_read(fdev); // dummy read + spi_flash_write_byte(fdev, SPI_CMD_RESET_MEMORY, protocol); + spi_flash_deselect(fdev); + ctrl_reg_read(fdev); // dummy read + ctrl_reg_read(fdev); // dummy read +} + +void spi_flash_release(struct flash_device *fdev) +{ + spi_flash_deselect(fdev); +} + +int spi_flash_init(struct flash_device *fdev) +{ + int ret = 0; + + if (!fdev) + return -1; + + spi_flash_reset(fdev, SPI_PROTO_STR); + + spi_flash_write_byte(fdev, SPI_CMD_READ_ID, SPI_PROTO_STR); + int mfr_id = spi_flash_read_byte(fdev, SPI_PROTO_STR); + int mem_type = spi_flash_read_byte(fdev, SPI_PROTO_STR); + int mem_capacity = spi_flash_read_byte(fdev, SPI_PROTO_STR); + spi_flash_deselect(fdev); + + printf("Manufacturer ID: 0x%02x\n", mfr_id); + printf("Memory type: 0x%02x\n", mem_type); + printf("Memory capacity: 0x%02x\n", mem_capacity); + + if (mfr_id == 0 || mfr_id == 0xff) { + fprintf(stderr, "Failed to read flash ID\n"); + spi_flash_deselect(fdev); + return -1; + } + + switch (mfr_id) { + case 0x20: + // Micron + printf("Manufacturer: Micron\n"); + // convert from BCD + mem_capacity = (mem_capacity & 0xf) + (((mem_capacity >> 4) & 0xf) * 10); + fdev->size = ((size_t)1) << (mem_capacity+6); + break; + case 0xC2: + // Macronix + printf("Manufacturer: Macronix\n"); + fdev->size = ((size_t)1) << (mem_capacity-32); + break; + default: + // unknown + fprintf(stderr, "Unknown flash ID\n"); + spi_flash_deselect(fdev); + return -1; + } + + printf("Flash size: %ld MB\n", fdev->size / (1 << 20)); + + fdev->protocol = SPI_PROTO_STR; + fdev->bulk_protocol = SPI_PROTO_STR; + fdev->read_dummy_cycles = 0; + fdev->write_buffer_size = SPI_PAGE_SIZE; + fdev->erase_block_size = SPI_SUBSECTOR_SIZE; + + printf("Write buffer size: %ld B\n", fdev->write_buffer_size); + printf("Erase block size: %ld B\n", fdev->erase_block_size); + + printf("Status register: 0x%02x\n", spi_flash_read_status_reg(fdev, SPI_PROTO_STR)); + + switch (mfr_id) { + case 0x20: + // Micron + printf("Flag status register: 0x%02x\n", spi_flash_read_flag_status_reg(fdev, SPI_PROTO_STR)); + printf("Volatile config register: 0x%02x\n", spi_flash_read_volatile_cfg_reg(fdev, SPI_PROTO_STR)); + printf("Global freeze bit: 0x%02x\n", spi_flash_read_global_freeze_bit(fdev, SPI_PROTO_STR)); + printf("Sector protection register: 0x%04x\n", spi_flash_read_sector_protection_reg(fdev, SPI_PROTO_STR)); + + if (fdev->data_width == 4) { + spi_flash_write_volatile_config_reg(fdev, 0xFB, SPI_PROTO_STR); + fdev->bulk_protocol = SPI_PROTO_QUAD_STR; + fdev->read_dummy_cycles = 10; + } + + break; + case 0xC2: + // Macronix + printf("Config register: 0x%02x\n", spi_mxic_flash_read_cfg_reg(fdev, SPI_PROTO_STR)); + printf("Sector protection register: 0x%04x\n", spi_flash_read_sector_protection_reg(fdev, SPI_PROTO_STR)); + printf("Security register: 0x%02x\n", spi_mxic_flash_read_security_reg(fdev, SPI_PROTO_STR)); + + if (fdev->data_width == 4) { + spi_mxic_flash_write_status_cfg_reg(fdev, 0x40, 0x07, SPI_PROTO_STR); + fdev->bulk_protocol = SPI_PROTO_QUAD_STR; + fdev->read_dummy_cycles = 6; + } + + break; + } + + spi_flash_release(fdev); + return ret; +} + +int spi_flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest) +{ + char *d = dest; + + int protocol = SPI_PROTO_STR; + + if (fdev->data_width == 4) { + protocol = SPI_PROTO_QUAD_STR; + } + + if (fdev->size > 0x1000000) { + // four byte address read + if (protocol == SPI_PROTO_QUAD_STR) { + spi_flash_write_byte(fdev, SPI_CMD_4B_FAST_READ_QUAD_IO, SPI_PROTO_STR); + } else { + spi_flash_write_byte(fdev, SPI_CMD_4B_READ, SPI_PROTO_STR); + } + spi_flash_write_addr_4b(fdev, addr, protocol); + } else { + // normal read + if (protocol == SPI_PROTO_QUAD_STR) { + spi_flash_write_byte(fdev, SPI_CMD_FAST_READ_QUAD_IO, SPI_PROTO_STR); + } else { + spi_flash_write_byte(fdev, SPI_CMD_READ, SPI_PROTO_STR); + } + spi_flash_write_addr(fdev, addr, protocol); + } + + if (protocol != SPI_PROTO_STR) { + // dummy cycles + for (int i = 0; i < fdev->read_dummy_cycles; i++) { + ctrl_reg_write(fdev, FLASH_CLK); + ctrl_reg_write(fdev, 0); + } + } + + while (len > 0) { + *d = spi_flash_read_byte(fdev, protocol); + len--; + d++; + } + + spi_flash_deselect(fdev); + + return 0; +} + +int spi_flash_write(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + const char *s = src; + + int protocol = SPI_PROTO_STR; + + if (fdev->data_width == 4) { + protocol = SPI_PROTO_QUAD_STR; + } + + while (len > 0) { + spi_flash_write_enable(fdev, SPI_PROTO_STR); + + if (!(spi_flash_read_status_reg(fdev, SPI_PROTO_STR) & 0x02)) { + fprintf(stderr, "Failed to enable writing\n"); + spi_flash_deselect(fdev); + return -1; + } + + if (fdev->size > 0x1000000) { + // four byte address page program + if (protocol == SPI_PROTO_QUAD_STR) { + spi_flash_write_byte(fdev, SPI_CMD_4B_PAGE_PROGRAM_QUAD_IN_EXT, SPI_PROTO_STR); + } else { + spi_flash_write_byte(fdev, SPI_CMD_4B_PAGE_PROGRAM, SPI_PROTO_STR); + } + spi_flash_write_addr_4b(fdev, addr, protocol); + } else { + // normal page program + if (protocol == SPI_PROTO_QUAD_STR) { + spi_flash_write_byte(fdev, SPI_CMD_PAGE_PROGRAM_QUAD_IN_EXT, SPI_PROTO_STR); + } else { + spi_flash_write_byte(fdev, SPI_CMD_PAGE_PROGRAM, SPI_PROTO_STR); + } + spi_flash_write_addr(fdev, addr, protocol); + } + + while (len > 0) { + spi_flash_write_byte(fdev, *s, protocol); + addr++; + s++; + len--; + + if ((addr & 0xff) == 0) + break; + } + + spi_flash_deselect(fdev); + + // wait for operation to complete + while (spi_flash_read_status_reg(fdev, SPI_PROTO_STR) & 0x01) {}; + } + + spi_flash_deselect(fdev); + + return 0; +} + +int spi_flash_erase(struct flash_device *fdev, size_t addr, size_t len) +{ + size_t erase_block_size = fdev->erase_block_size; + + while (len > 0) { + // determine sector size + erase_block_size = 0; + + if ((addr & (SPI_SECTOR_SIZE-1)) == 0 && len >= SPI_SECTOR_SIZE) { + erase_block_size = SPI_SECTOR_SIZE; + } else if ((addr & (SPI_SUBSECTOR_SIZE-1)) == 0 && len >= SPI_SUBSECTOR_SIZE) { + erase_block_size = SPI_SUBSECTOR_SIZE; + } + + // check size and alignment + if (!erase_block_size) { + fprintf(stderr, "Invalid erase request\n"); + spi_flash_deselect(fdev); + return -1; + } + + // enable writing + spi_flash_write_enable(fdev, SPI_PROTO_STR); + + if (!(spi_flash_read_status_reg(fdev, SPI_PROTO_STR) & 0x02)) { + fprintf(stderr, "Failed to enable writing\n"); + spi_flash_deselect(fdev); + return -1; + } + + // block erase + if (fdev->size > 0x1000000) { + if (erase_block_size == SPI_SECTOR_SIZE) { + // four byte address sector erase + spi_flash_write_byte(fdev, SPI_CMD_4B_SECTOR_ERASE, SPI_PROTO_STR); + spi_flash_write_addr_4b(fdev, addr, SPI_PROTO_STR); + } else if (erase_block_size == SPI_SUBSECTOR_SIZE) { + // normal 4KB subsector erase + spi_flash_write_byte(fdev, SPI_CMD_4B_4KB_SUBSECTOR_ERASE, SPI_PROTO_STR); + spi_flash_write_addr_4b(fdev, addr, SPI_PROTO_STR); + } + } else { + if (erase_block_size == SPI_SECTOR_SIZE) { + // normal sector erase + spi_flash_write_byte(fdev, SPI_CMD_SECTOR_ERASE, SPI_PROTO_STR); + spi_flash_write_addr(fdev, addr, SPI_PROTO_STR); + } else if (erase_block_size == SPI_SUBSECTOR_SIZE) { + // normal 4KB subsector erase + spi_flash_write_byte(fdev, SPI_CMD_4KB_SUBSECTOR_ERASE, SPI_PROTO_STR); + spi_flash_write_addr(fdev, addr, SPI_PROTO_STR); + } + } + + spi_flash_deselect(fdev); + + // wait for operation to complete + while (spi_flash_read_status_reg(fdev, SPI_PROTO_STR) & 0x01) {}; + + if (len <= erase_block_size) + break; + + addr += erase_block_size; + len -= erase_block_size; + } + + spi_flash_deselect(fdev); + + return 0; +} + +const struct flash_driver spi_flash_driver = { + .init = spi_flash_init, + .release = spi_flash_release, + .read = spi_flash_read, + .write = spi_flash_write, + .erase = spi_flash_erase +}; diff --git a/src/pyrite/utils/fpga_id.c b/src/pyrite/utils/fpga_id.c new file mode 100644 index 0000000..937527e --- /dev/null +++ b/src/pyrite/utils/fpga_id.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include "fpga_id.h" + +struct fpga_id { + int id; + int mask; + char part[16]; +}; + +const struct fpga_id fpga_id_list[] = +{ + // Xilinx + // Spartan 7 + {FPGA_ID_XC7S6, FPGA_ID_MASK_NOVER, "XC7S6"}, + {FPGA_ID_XC7S15, FPGA_ID_MASK_NOVER, "XC7S15"}, + {FPGA_ID_XC7S25, FPGA_ID_MASK_NOVER, "XC7S25"}, + {FPGA_ID_XC7S50, FPGA_ID_MASK_NOVER, "XC7S50"}, + {FPGA_ID_XC7S75, FPGA_ID_MASK_NOVER, "XC7S75"}, + {FPGA_ID_XC7S100, FPGA_ID_MASK_NOVER, "XC7S100"}, + // Artix 7 + {FPGA_ID_XC7A15T, FPGA_ID_MASK_NOVER, "XC7A15T"}, + {FPGA_ID_XC7A35T, FPGA_ID_MASK_NOVER, "XC7A35T"}, + {FPGA_ID_XC7A50T, FPGA_ID_MASK_NOVER, "XC7A50T"}, + {FPGA_ID_XC7A75T, FPGA_ID_MASK_NOVER, "XC7A75T"}, + {FPGA_ID_XC7A100T, FPGA_ID_MASK_NOVER, "XC7A100T"}, + {FPGA_ID_XC7A200T, FPGA_ID_MASK_NOVER, "XC7A200T"}, + // Kintex 7 + {FPGA_ID_XC7K70T, FPGA_ID_MASK_NOVER, "XC7K70T"}, + {FPGA_ID_XC7K160T, FPGA_ID_MASK_NOVER, "XC7K160T"}, + {FPGA_ID_XC7K325T, FPGA_ID_MASK_NOVER, "XC7K325T"}, + {FPGA_ID_XC7K355T, FPGA_ID_MASK_NOVER, "XC7K355T"}, + {FPGA_ID_XC7K410T, FPGA_ID_MASK_NOVER, "XC7K410T"}, + {FPGA_ID_XC7K420T, FPGA_ID_MASK_NOVER, "XC7K420T"}, + {FPGA_ID_XC7K480T, FPGA_ID_MASK_NOVER, "XC7K480T"}, + // Virtex 7 + {FPGA_ID_XC7V585T, FPGA_ID_MASK_NOVER, "XC7V585T"}, + {FPGA_ID_XC7V2000T, FPGA_ID_MASK_NOVER, "XC7V2000T"}, + {FPGA_ID_XC7VX330T, FPGA_ID_MASK_NOVER, "XC7VX330T"}, + {FPGA_ID_XC7VX415T, FPGA_ID_MASK_NOVER, "XC7VX415T"}, + {FPGA_ID_XC7VX485T, FPGA_ID_MASK_NOVER, "XC7VX485T"}, + {FPGA_ID_XC7VX550T, FPGA_ID_MASK_NOVER, "XC7VX550T"}, + {FPGA_ID_XC7VX690T, FPGA_ID_MASK_NOVER, "XC7VX690T"}, + {FPGA_ID_XC7VX980T, FPGA_ID_MASK_NOVER, "XC7VX980T"}, + {FPGA_ID_XC7VX1140T, FPGA_ID_MASK_NOVER, "XC7VX1140T"}, + {FPGA_ID_XC7VH580T, FPGA_ID_MASK_NOVER, "XC7VH580T"}, + {FPGA_ID_XC7VH870T, FPGA_ID_MASK_NOVER, "XC7VH870T"}, + // Zynq 7000 + {FPGA_ID_XC7Z007, FPGA_ID_MASK_NOVER, "XC7Z007"}, + {FPGA_ID_XC7Z010, FPGA_ID_MASK_NOVER, "XC7Z010"}, + {FPGA_ID_XC7Z012, FPGA_ID_MASK_NOVER, "XC7Z012"}, + {FPGA_ID_XC7Z014, FPGA_ID_MASK_NOVER, "XC7Z014"}, + {FPGA_ID_XC7Z015, FPGA_ID_MASK_NOVER, "XC7Z015"}, + {FPGA_ID_XC7Z020, FPGA_ID_MASK_NOVER, "XC7Z020"}, + {FPGA_ID_XC7Z030, FPGA_ID_MASK_NOVER, "XC7Z030"}, + {FPGA_ID_XC7Z035, FPGA_ID_MASK_NOVER, "XC7Z035"}, + {FPGA_ID_XC7Z045, FPGA_ID_MASK_NOVER, "XC7Z045"}, + {FPGA_ID_XC7Z100, FPGA_ID_MASK_NOVER, "XC7Z100"}, + // Kintex UltraScale + {FPGA_ID_XCKU025, FPGA_ID_MASK_NOVER, "XCKU025"}, + {FPGA_ID_XCKU035, FPGA_ID_MASK_NOVER, "XCKU035"}, + {FPGA_ID_XCKU040, FPGA_ID_MASK_NOVER, "XCKU040"}, + {FPGA_ID_XCKU060, FPGA_ID_MASK_NOVER, "XCKU060"}, + {FPGA_ID_XCKU085, FPGA_ID_MASK_NOVER, "XCKU085"}, + {FPGA_ID_XCKU095, FPGA_ID_MASK_NOVER, "XCKU095"}, + {FPGA_ID_XCKU115, FPGA_ID_MASK_NOVER, "XCKU115"}, + // Virtex UltraScale + {FPGA_ID_XCVU065, FPGA_ID_MASK_NOVER, "XCVU065"}, + {FPGA_ID_XCVU080, FPGA_ID_MASK_NOVER, "XCVU080"}, + {FPGA_ID_XCVU095, FPGA_ID_MASK_NOVER, "XCVU095"}, + {FPGA_ID_XCVU125, FPGA_ID_MASK_NOVER, "XCVU125"}, + {FPGA_ID_XCVU160, FPGA_ID_MASK_NOVER, "XCVU160"}, + {FPGA_ID_XCVU190, FPGA_ID_MASK_NOVER, "XCVU190"}, + {FPGA_ID_XCVU440, FPGA_ID_MASK_NOVER, "XCVU440"}, + // Artix UltraScale+ + {FPGA_ID_XCAU10P, FPGA_ID_MASK_NOVER, "XCAU10P"}, + {FPGA_ID_XCAU15P, FPGA_ID_MASK_NOVER, "XCAU15P"}, + {FPGA_ID_XCAU20P, FPGA_ID_MASK_NOVER, "XCAU20P"}, + {FPGA_ID_XCAU25P, FPGA_ID_MASK_NOVER, "XCAU25P"}, + // Kintex UltraScale+ + {FPGA_ID_XCKU3P, FPGA_ID_MASK_NOVER, "XCKU3P"}, + {FPGA_ID_XCKU5P, FPGA_ID_MASK_NOVER, "XCKU5P"}, + {FPGA_ID_XCKU9P, FPGA_ID_MASK_NOVER, "XCKU9P"}, + {FPGA_ID_XCKU11P, FPGA_ID_MASK_NOVER, "XCKU11P"}, + {FPGA_ID_XCKU13P, FPGA_ID_MASK_NOVER, "XCKU13P"}, + {FPGA_ID_XCKU15P, FPGA_ID_MASK_NOVER, "XCKU15P"}, + // Virtex UltraScale+ + {FPGA_ID_XCVU3P, FPGA_ID_MASK_NOVER, "XCVU3P"}, + {FPGA_ID_XCVU5P, FPGA_ID_MASK_NOVER, "XCVU5P"}, + {FPGA_ID_XCVU7P, FPGA_ID_MASK_NOVER, "XCVU7P"}, + {FPGA_ID_XCVU9P, FPGA_ID_MASK_NOVER, "XCVU9P"}, + {FPGA_ID_XCVU11P, FPGA_ID_MASK_NOVER, "XCVU11P"}, + {FPGA_ID_XCVU13P, FPGA_ID_MASK_NOVER, "XCVU13P"}, + {FPGA_ID_XCVU19P, FPGA_ID_MASK_NOVER, "XCVU19P"}, + {FPGA_ID_XCVU23P, FPGA_ID_MASK_NOVER, "XCVU23P"}, + {FPGA_ID_XCVU27P, FPGA_ID_MASK_NOVER, "XCVU27P"}, + {FPGA_ID_XCVU29P, FPGA_ID_MASK_NOVER, "XCVU29P"}, + {FPGA_ID_XCVU31P, FPGA_ID_MASK_NOVER, "XCVU31P"}, + {FPGA_ID_XCVU33P, FPGA_ID_MASK_NOVER, "XCVU33P"}, + {FPGA_ID_XCVU35P, FPGA_ID_MASK_NOVER, "XCVU35P"}, + {FPGA_ID_XCVU37P, FPGA_ID_MASK_NOVER, "XCVU37P"}, + {FPGA_ID_XCVU45P, FPGA_ID_MASK_NOVER, "XCVU45P"}, + {FPGA_ID_XCVU47P, FPGA_ID_MASK_NOVER, "XCVU47P"}, + {FPGA_ID_XCVU57P, FPGA_ID_MASK_NOVER, "XCVU57P"}, + // Zynq UltraScale+ + {FPGA_ID_XCZU1, FPGA_ID_MASK_NOVER, "XCZU1"}, + {FPGA_ID_XCZU2, FPGA_ID_MASK_NOVER, "XCZU2"}, + {FPGA_ID_XCZU3, FPGA_ID_MASK_NOVER, "XCZU3"}, + {FPGA_ID_XCZU4, FPGA_ID_MASK_NOVER, "XCZU4"}, + {FPGA_ID_XCZU5, FPGA_ID_MASK_NOVER, "XCZU5"}, + {FPGA_ID_XCZU6, FPGA_ID_MASK_NOVER, "XCZU6"}, + {FPGA_ID_XCZU7, FPGA_ID_MASK_NOVER, "XCZU7"}, + {FPGA_ID_XCZU9, FPGA_ID_MASK_NOVER, "XCZU9"}, + {FPGA_ID_XCZU11, FPGA_ID_MASK_NOVER, "XCZU11"}, + {FPGA_ID_XCZU15, FPGA_ID_MASK_NOVER, "XCZU15"}, + {FPGA_ID_XCZU17, FPGA_ID_MASK_NOVER, "XCZU17"}, + {FPGA_ID_XCZU19, FPGA_ID_MASK_NOVER, "XCZU19"}, + {FPGA_ID_XCZU21, FPGA_ID_MASK_NOVER, "XCZU21"}, + {FPGA_ID_XCZU25, FPGA_ID_MASK_NOVER, "XCZU25"}, + {FPGA_ID_XCZU27, FPGA_ID_MASK_NOVER, "XCZU27"}, + {FPGA_ID_XCZU28, FPGA_ID_MASK_NOVER, "XCZU28"}, + {FPGA_ID_XCZU29, FPGA_ID_MASK_NOVER, "XCZU29"}, + {FPGA_ID_XCZU39, FPGA_ID_MASK_NOVER, "XCZU39"}, + {FPGA_ID_XCZU43, FPGA_ID_MASK_NOVER, "XCZU43"}, + {FPGA_ID_XCZU46, FPGA_ID_MASK_NOVER, "XCZU46"}, + {FPGA_ID_XCZU47, FPGA_ID_MASK_NOVER, "XCZU47"}, + {FPGA_ID_XCZU48, FPGA_ID_MASK_NOVER, "XCZU48"}, + {FPGA_ID_XCZU49, FPGA_ID_MASK_NOVER, "XCZU49"}, + {FPGA_ID_XCZU65, FPGA_ID_MASK_NOVER, "XCZU65"}, + {FPGA_ID_XCZU67, FPGA_ID_MASK_NOVER, "XCZU67"}, + // Kria SoM (Zynq UltraScale+) + {FPGA_ID_XCK26, FPGA_ID_MASK_NOVER, "XCK26"}, + // Alveo (Virtex UltraScale+) + {FPGA_ID_XCU26_XCUX35, FPGA_ID_MASK_NOVER, "XCU26_XCUX35"}, + {FPGA_ID_XCU50_XCU55N, FPGA_ID_MASK_NOVER, "XCU50_XCU55N"}, + {FPGA_ID_XCU200, FPGA_ID_MASK_NOVER, "XCU200"}, + {FPGA_ID_XCU250, FPGA_ID_MASK_NOVER, "XCU250"}, + {FPGA_ID_XCU280_XCU55C, FPGA_ID_MASK_NOVER, "XCU280_XCU55C"}, + // Versal AI Edge + {FPGA_ID_XCVE1752, FPGA_ID_MASK_NOVER, "XCVE1752"}, + {FPGA_ID_XCVE2002, FPGA_ID_MASK_NOVER, "XCVE2002"}, + {FPGA_ID_XCVE2102, FPGA_ID_MASK_NOVER, "XCVE2102"}, + {FPGA_ID_XCVE2202, FPGA_ID_MASK_NOVER, "XCVE2202"}, + {FPGA_ID_XCVE2302, FPGA_ID_MASK_NOVER, "XCVE2302"}, + {FPGA_ID_XCVE2602, FPGA_ID_MASK_NOVER, "XCVE2602"}, + {FPGA_ID_XCVE2802, FPGA_ID_MASK_NOVER, "XCVE2802"}, + // Versal AI Core + {FPGA_ID_XCVC1352, FPGA_ID_MASK_NOVER, "XCVC1352"}, + {FPGA_ID_XCVC1502, FPGA_ID_MASK_NOVER, "XCVC1502"}, + {FPGA_ID_XCVC1702, FPGA_ID_MASK_NOVER, "XCVC1702"}, + {FPGA_ID_XCVC1802, FPGA_ID_MASK_NOVER, "XCVC1802"}, + {FPGA_ID_XCVC1902, FPGA_ID_MASK_NOVER, "XCVC1902"}, + {FPGA_ID_XCVC2602, FPGA_ID_MASK_NOVER, "XCVC2602"}, + {FPGA_ID_XCVC2802, FPGA_ID_MASK_NOVER, "XCVC2802"}, + // Versal Prime + {FPGA_ID_XCVM1102, FPGA_ID_MASK_NOVER, "XCVM1102"}, + {FPGA_ID_XCVM1302, FPGA_ID_MASK_NOVER, "XCVM1302"}, + {FPGA_ID_XCVM1402, FPGA_ID_MASK_NOVER, "XCVM1402"}, + {FPGA_ID_XCVM1502, FPGA_ID_MASK_NOVER, "XCVM1502"}, + {FPGA_ID_XCVM1802, FPGA_ID_MASK_NOVER, "XCVM1802"}, + {FPGA_ID_XCVM2202, FPGA_ID_MASK_NOVER, "XCVM2202"}, + {FPGA_ID_XCVM2302, FPGA_ID_MASK_NOVER, "XCVM2302"}, + {FPGA_ID_XCVM2502, FPGA_ID_MASK_NOVER, "XCVM2502"}, + {FPGA_ID_XCVM2902, FPGA_ID_MASK_NOVER, "XCVM2902"}, + // Versal Premium + {FPGA_ID_XCVP1002, FPGA_ID_MASK_NOVER, "XCVP1002"}, + {FPGA_ID_XCVP1052, FPGA_ID_MASK_NOVER, "XCVP1052"}, + {FPGA_ID_XCVP1102, FPGA_ID_MASK_NOVER, "XCVP1102"}, + {FPGA_ID_XCVP1202, FPGA_ID_MASK_NOVER, "XCVP1202"}, + {FPGA_ID_XCVP1402, FPGA_ID_MASK_NOVER, "XCVP1402"}, + {FPGA_ID_XCVP1502, FPGA_ID_MASK_NOVER, "XCVP1502"}, + {FPGA_ID_XCVP1552, FPGA_ID_MASK_NOVER, "XCVP1552"}, + {FPGA_ID_XCVP1702, FPGA_ID_MASK_NOVER, "XCVP1702"}, + {FPGA_ID_XCVP1802, FPGA_ID_MASK_NOVER, "XCVP1802"}, + {FPGA_ID_XCVP2502, FPGA_ID_MASK_NOVER, "XCVP2502"}, + {FPGA_ID_XCVP2802, FPGA_ID_MASK_NOVER, "XCVP2802"}, + + // Intel + // Stratix 10 + {FPGA_ID_1SG10MH_U1, FPGA_ID_MASK_FULL, "1SG10MH_U1"}, + {FPGA_ID_1SG10MH_U2, FPGA_ID_MASK_FULL, "1SG10MH_U2"}, + {FPGA_ID_1SG040H, FPGA_ID_MASK_FULL, "1SG040H"}, + {FPGA_ID_1SG040H_NL, FPGA_ID_MASK_FULL, "1SG040H(NL)"}, + {FPGA_ID_1SG065H, FPGA_ID_MASK_FULL, "1SG065H"}, + {FPGA_ID_1SG065H_NL, FPGA_ID_MASK_FULL, "1SG065H(NL)"}, + {FPGA_ID_1SG085H, FPGA_ID_MASK_FULL, "1SG085H"}, + {FPGA_ID_1SG110H, FPGA_ID_MASK_FULL, "1SG110H"}, + {FPGA_ID_1SG110H_NL, FPGA_ID_MASK_FULL, "1SG110H(NL)"}, + {FPGA_ID_1SG165H, FPGA_ID_MASK_FULL, "1SG165H"}, + {FPGA_ID_1SG166H, FPGA_ID_MASK_FULL, "1SG166H"}, + {FPGA_ID_1SG166H_NL, FPGA_ID_MASK_FULL, "1SG166H(NL)"}, + {FPGA_ID_1SG210H, FPGA_ID_MASK_FULL, "1SG210H"}, + {FPGA_ID_1SG210H_ES1, FPGA_ID_MASK_FULL, "1SG210H(ES1)"}, + {FPGA_ID_1SG211H, FPGA_ID_MASK_FULL, "1SG211H"}, + {FPGA_ID_1SG250L, FPGA_ID_MASK_FULL, "1SG250L"}, + {FPGA_ID_1SG250H, FPGA_ID_MASK_FULL, "1SG250H"}, + {FPGA_ID_1SG280L, FPGA_ID_MASK_FULL, "1SG280L"}, + {FPGA_ID_1SG280L_NL, FPGA_ID_MASK_FULL, "1SG280L(NL)"}, + {FPGA_ID_1SG280L_ES1, FPGA_ID_MASK_FULL, "1SG280L(ES1)"}, + {FPGA_ID_1SG280L_ES2, FPGA_ID_MASK_FULL, "1SG280L(ES2)"}, + {FPGA_ID_1SG280L_ES3, FPGA_ID_MASK_FULL, "1SG280L(ES3)"}, + {FPGA_ID_1SG280H, FPGA_ID_MASK_FULL, "1SG280H"}, + {FPGA_ID_1SG280H_NL, FPGA_ID_MASK_FULL, "1SG280H(NL)"}, + {FPGA_ID_1SG280H_ES1, FPGA_ID_MASK_FULL, "1SG280H(ES1)"}, + {FPGA_ID_1SG280H_ES2, FPGA_ID_MASK_FULL, "1SG280H(ES2)"}, + {FPGA_ID_1SG280H_ES3, FPGA_ID_MASK_FULL, "1SG280H(ES3)"}, + {FPGA_ID_1SX040H, FPGA_ID_MASK_FULL, "1SX040H"}, + {FPGA_ID_1SX065H, FPGA_ID_MASK_FULL, "1SX065H"}, + {FPGA_ID_1SX085H, FPGA_ID_MASK_FULL, "1SX085H"}, + {FPGA_ID_1SX110H, FPGA_ID_MASK_FULL, "1SX110H"}, + {FPGA_ID_1SX165H, FPGA_ID_MASK_FULL, "1SX165H"}, + {FPGA_ID_1SX210H, FPGA_ID_MASK_FULL, "1SX210H"}, + {FPGA_ID_1SX250L, FPGA_ID_MASK_FULL, "1SX250L"}, + {FPGA_ID_1SX250H, FPGA_ID_MASK_FULL, "1SX250H"}, + {FPGA_ID_1SX280L, FPGA_ID_MASK_FULL, "1SX280L"}, + {FPGA_ID_1SX280L_ES1, FPGA_ID_MASK_FULL, "1SX280L(ES1)"}, + {FPGA_ID_1SX280L_ES2, FPGA_ID_MASK_FULL, "1SX280L(ES2)"}, + {FPGA_ID_1SX280H, FPGA_ID_MASK_FULL, "1SX280H"}, + {FPGA_ID_1SX280H_ES1, FPGA_ID_MASK_FULL, "1SX280H(ES1)"}, + {FPGA_ID_1SX280H_ES2, FPGA_ID_MASK_FULL, "1SX280H(ES2)"}, + {FPGA_ID_1ST040E, FPGA_ID_MASK_FULL, "1ST040E"}, + {FPGA_ID_1ST040E_NL, FPGA_ID_MASK_FULL, "1ST040E(NL)"}, + {FPGA_ID_1ST085E, FPGA_ID_MASK_FULL, "1ST085E"}, + {FPGA_ID_1ST110E, FPGA_ID_MASK_FULL, "1ST110E"}, + {FPGA_ID_1ST110E_NL, FPGA_ID_MASK_FULL, "1ST110E(NL)"}, + {FPGA_ID_1ST165E, FPGA_ID_MASK_FULL, "1ST165E"}, + {FPGA_ID_1ST210E, FPGA_ID_MASK_FULL, "1ST210E"}, + {FPGA_ID_1ST210E_ES1, FPGA_ID_MASK_FULL, "1ST210E(ES1)"}, + {FPGA_ID_1ST250E, FPGA_ID_MASK_FULL, "1ST250E"}, + {FPGA_ID_1ST280E, FPGA_ID_MASK_FULL, "1ST280E"}, + {FPGA_ID_1ST280E_ES1, FPGA_ID_MASK_FULL, "1ST280E(ES1)"}, + {FPGA_ID_1SM16BE, FPGA_ID_MASK_FULL, "1SM16BE"}, + {FPGA_ID_1SM16BE_ES1, FPGA_ID_MASK_FULL, "1SM16BE(ES1)"}, + {FPGA_ID_1SM16BH, FPGA_ID_MASK_FULL, "1SM16BH"}, + {FPGA_ID_1SM16BH_ES1, FPGA_ID_MASK_FULL, "1SM16BH(ES1)"}, + {FPGA_ID_1SM16CH, FPGA_ID_MASK_FULL, "1SM16CH"}, + {FPGA_ID_1SM16CH_ES1, FPGA_ID_MASK_FULL, "1SM16CH(ES1)"}, + {FPGA_ID_1SM21BE, FPGA_ID_MASK_FULL, "1SM21BE"}, + {FPGA_ID_1SM21BE_ES1, FPGA_ID_MASK_FULL, "1SM21BE(ES1)"}, + {FPGA_ID_1SM21BH, FPGA_ID_MASK_FULL, "1SM21BH"}, + {FPGA_ID_1SM21BH_ES1, FPGA_ID_MASK_FULL, "1SM21BH(ES1)"}, + {FPGA_ID_1SM21CH, FPGA_ID_MASK_FULL, "1SM21CH"}, + {FPGA_ID_1SM21CH_ES1, FPGA_ID_MASK_FULL, "1SM21CH(ES1)"}, + {FPGA_ID_1SD110P, FPGA_ID_MASK_FULL, "1SD110P"}, + {FPGA_ID_1SD110P_NL, FPGA_ID_MASK_FULL, "1SD110P(NL)"}, + {FPGA_ID_1SD21BP, FPGA_ID_MASK_FULL, "1SD21BP"}, + {FPGA_ID_1SD280P, FPGA_ID_MASK_FULL, "1SD280P"}, + // Agilex + {FPGA_ID_AGFA006R16A, FPGA_ID_MASK_FULL, "AGFA006R16A"}, + {FPGA_ID_AGFA008R16A, FPGA_ID_MASK_FULL, "AGFA008R16A"}, + {FPGA_ID_AGFA012R24A, FPGA_ID_MASK_FULL, "AGFA012R24A"}, + {FPGA_ID_AGFA012R24B, FPGA_ID_MASK_FULL, "AGFA012R24B"}, + {FPGA_ID_AGFA014R24AR0, FPGA_ID_MASK_FULL, "AGFA014R24AR0"}, + {FPGA_ID_AGFA014R24A, FPGA_ID_MASK_FULL, "AGFA014R24A"}, + {FPGA_ID_AGFA014R24B, FPGA_ID_MASK_FULL, "AGFA014R24B"}, + {FPGA_ID_AGFA019R25A, FPGA_ID_MASK_FULL, "AGFA019R25A"}, + {FPGA_ID_AGFA022R24C, FPGA_ID_MASK_FULL, "AGFA022R24C"}, + {FPGA_ID_AGFA022R25A, FPGA_ID_MASK_FULL, "AGFA022R25A"}, + {FPGA_ID_AGFA022R31C, FPGA_ID_MASK_FULL, "AGFA022R31C"}, + {FPGA_ID_AGFA023R25AR0, FPGA_ID_MASK_FULL, "AGFA023R25AR0"}, + {FPGA_ID_AGFA023R25A, FPGA_ID_MASK_FULL, "AGFA023R25A"}, + {FPGA_ID_AGFA027R24CR0, FPGA_ID_MASK_FULL, "AGFA027R24CR0"}, + {FPGA_ID_AGFA027R24CR2, FPGA_ID_MASK_FULL, "AGFA027R24CR2"}, + {FPGA_ID_AGFA027R24C, FPGA_ID_MASK_FULL, "AGFA027R24C"}, + {FPGA_ID_AGFA027R25AR0, FPGA_ID_MASK_FULL, "AGFA027R25AR0"}, + {FPGA_ID_AGFA027R25A, FPGA_ID_MASK_FULL, "AGFA027R25A"}, + {FPGA_ID_AGFA027R31C, FPGA_ID_MASK_FULL, "AGFA027R31C"}, + {FPGA_ID_AGFB006R16A, FPGA_ID_MASK_FULL, "AGFB006R16A"}, + {FPGA_ID_AGFB008R16A, FPGA_ID_MASK_FULL, "AGFB008R16A"}, + {FPGA_ID_AGFB012R24A, FPGA_ID_MASK_FULL, "AGFB012R24A"}, + {FPGA_ID_AGFB012R24B, FPGA_ID_MASK_FULL, "AGFB012R24B"}, + {FPGA_ID_AGFB014R24AR0, FPGA_ID_MASK_FULL, "AGFB014R24AR0"}, + {FPGA_ID_AGFB014R24A, FPGA_ID_MASK_FULL, "AGFB014R24A"}, + {FPGA_ID_AGFB014R24B, FPGA_ID_MASK_FULL, "AGFB014R24B"}, + {FPGA_ID_AGFB019R25A, FPGA_ID_MASK_FULL, "AGFB019R25A"}, + {FPGA_ID_AGFB022R24C, FPGA_ID_MASK_FULL, "AGFB022R24C"}, + {FPGA_ID_AGFB022R25A, FPGA_ID_MASK_FULL, "AGFB022R25A"}, + {FPGA_ID_AGFB022R31C, FPGA_ID_MASK_FULL, "AGFB022R31C"}, + {FPGA_ID_AGFB023R25AR0, FPGA_ID_MASK_FULL, "AGFB023R25AR0"}, + {FPGA_ID_AGFB023R25A, FPGA_ID_MASK_FULL, "AGFB023R25A"}, + {FPGA_ID_AGFB027R24CR0, FPGA_ID_MASK_FULL, "AGFB027R24CR0"}, + {FPGA_ID_AGFB027R24CR2, FPGA_ID_MASK_FULL, "AGFB027R24CR2"}, + {FPGA_ID_AGFB027R24C, FPGA_ID_MASK_FULL, "AGFB027R24C"}, + {FPGA_ID_AGFB027R25AR0, FPGA_ID_MASK_FULL, "AGFB027R25AR0"}, + {FPGA_ID_AGFB027R25A, FPGA_ID_MASK_FULL, "AGFB027R25A"}, + {FPGA_ID_AGFB027R31C, FPGA_ID_MASK_FULL, "AGFB027R31C"}, + {FPGA_ID_AGFC019R25A, FPGA_ID_MASK_FULL, "AGFC019R25A"}, + {FPGA_ID_AGFC023R25AR0, FPGA_ID_MASK_FULL, "AGFC023R25AR0"}, + {FPGA_ID_AGFC023R25A, FPGA_ID_MASK_FULL, "AGFC023R25A"}, + {FPGA_ID_AGFD019R25A, FPGA_ID_MASK_FULL, "AGFD019R25A"}, + {FPGA_ID_AGFD023R25AR0, FPGA_ID_MASK_FULL, "AGFD023R25AR0"}, + {FPGA_ID_AGFD023R25A, FPGA_ID_MASK_FULL, "AGFD023R25A"}, + {FPGA_ID_AGIB022R29A, FPGA_ID_MASK_FULL, "AGIB022R29A"}, + {FPGA_ID_AGIB022R31B, FPGA_ID_MASK_FULL, "AGIB022R31B"}, + {FPGA_ID_AGIB027R29AR0, FPGA_ID_MASK_FULL, "AGIB027R29AR0"}, + {FPGA_ID_AGIB027R29AR1, FPGA_ID_MASK_FULL, "AGIB027R29AR1"}, + {FPGA_ID_AGIB027R29AR3, FPGA_ID_MASK_FULL, "AGIB027R29AR3"}, + {FPGA_ID_AGIB027R29A, FPGA_ID_MASK_FULL, "AGIB027R29A"}, + {FPGA_ID_AGIB027R31BR0, FPGA_ID_MASK_FULL, "AGIB027R31BR0"}, + {FPGA_ID_AGIB027R31B, FPGA_ID_MASK_FULL, "AGIB027R31B"}, + + // end of list + {0, 0, ""} +}; + +const char *get_fpga_part(int id) +{ + const struct fpga_id *ptr = fpga_id_list; + + while (ptr->id && ((ptr->id ^ id) & ptr->mask) != 0) { + ptr++; + } + + return ptr->part; +} diff --git a/src/pyrite/utils/fpga_id.h b/src/pyrite/utils/fpga_id.h new file mode 100644 index 0000000..2c0bc0b --- /dev/null +++ b/src/pyrite/utils/fpga_id.h @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: GPL */ +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#ifndef FPGA_ID_H +#define FPGA_ID_H + +#define FPGA_ID_MASK_FULL 0xFFFFFFFF +#define FPGA_ID_MASK_VER 0xF0000000 +#define FPGA_ID_MASK_PART 0x0FFFF000 +#define FPGA_ID_MASK_MFR 0x00000FFE +#define FPGA_ID_MASK_NOVER 0x0FFFFFFF + +// Xilinx +// Spartan 7 +#define FPGA_ID_XC7S6 0x3622093 +#define FPGA_ID_XC7S15 0x3620093 +#define FPGA_ID_XC7S25 0x37C4093 +#define FPGA_ID_XC7S50 0x362F093 +#define FPGA_ID_XC7S75 0x37C8093 +#define FPGA_ID_XC7S100 0x37C7093 +// Artix 7 +#define FPGA_ID_XC7A15T 0x362D093 +#define FPGA_ID_XC7A35T 0x362D093 +#define FPGA_ID_XC7A50T 0x362C093 +#define FPGA_ID_XC7A75T 0x3632093 +#define FPGA_ID_XC7A100T 0x3631093 +#define FPGA_ID_XC7A200T 0x3636093 +// Kintex 7 +#define FPGA_ID_XC7K70T 0x3647093 +#define FPGA_ID_XC7K160T 0x364C093 +#define FPGA_ID_XC7K325T 0x3651093 +#define FPGA_ID_XC7K355T 0x3747093 +#define FPGA_ID_XC7K410T 0x3656093 +#define FPGA_ID_XC7K420T 0x3752093 +#define FPGA_ID_XC7K480T 0x3751093 +// Virtex 7 +#define FPGA_ID_XC7V585T 0x3671093 +#define FPGA_ID_XC7V2000T 0x36B3093 +#define FPGA_ID_XC7VX330T 0x3667093 +#define FPGA_ID_XC7VX415T 0x3682093 +#define FPGA_ID_XC7VX485T 0x3687093 +#define FPGA_ID_XC7VX550T 0x3692093 +#define FPGA_ID_XC7VX690T 0x3691093 +#define FPGA_ID_XC7VX980T 0x3696093 +#define FPGA_ID_XC7VX1140T 0x36D5093 +#define FPGA_ID_XC7VH580T 0x36D9093 +#define FPGA_ID_XC7VH870T 0x36DB093 +// Zynq 7000 +#define FPGA_ID_XC7Z007 0x3723093 +#define FPGA_ID_XC7Z010 0x3722093 +#define FPGA_ID_XC7Z012 0x373C093 +#define FPGA_ID_XC7Z014 0x3728093 +#define FPGA_ID_XC7Z015 0x373B093 +#define FPGA_ID_XC7Z020 0x3727093 +#define FPGA_ID_XC7Z030 0x372C093 +#define FPGA_ID_XC7Z035 0x3732093 +#define FPGA_ID_XC7Z045 0x3731093 +#define FPGA_ID_XC7Z100 0x3736093 +// Kintex UltraScale +#define FPGA_ID_XCKU025 0x3824093 +#define FPGA_ID_XCKU035 0x3823093 +#define FPGA_ID_XCKU040 0x3822093 +#define FPGA_ID_XCKU060 0x3919093 +#define FPGA_ID_XCKU085 0x380F093 +#define FPGA_ID_XCKU095 0x3844093 +#define FPGA_ID_XCKU115 0x390D093 +// Virtex UltraScale +#define FPGA_ID_XCVU065 0x3939093 +#define FPGA_ID_XCVU080 0x3843093 +#define FPGA_ID_XCVU095 0x3842093 +#define FPGA_ID_XCVU125 0x392D093 +#define FPGA_ID_XCVU160 0x3933093 +#define FPGA_ID_XCVU190 0x3931093 +#define FPGA_ID_XCVU440 0x396D093 +// Artix UltraScale+ +#define FPGA_ID_XCAU10P 0x4AC5093 +#define FPGA_ID_XCAU15P 0x4AC4093 +#define FPGA_ID_XCAU20P 0x4A65093 +#define FPGA_ID_XCAU25P 0x4A64093 +// Kintex UltraScale+ +#define FPGA_ID_XCKU3P 0x4A63093 +#define FPGA_ID_XCKU5P 0x4A62093 +#define FPGA_ID_XCKU9P 0x484A093 +#define FPGA_ID_XCKU11P 0x4A4E093 +#define FPGA_ID_XCKU13P 0x4A52093 +#define FPGA_ID_XCKU15P 0x4A56093 +#define FPGA_ID_XCKU19P 0x4ACF093 +// Virtex UltraScale+ +#define FPGA_ID_XCVU3P 0x4B39093 +#define FPGA_ID_XCVU5P 0x4B2B093 +#define FPGA_ID_XCVU7P 0x4B29093 +#define FPGA_ID_XCVU9P 0x4B31093 +#define FPGA_ID_XCVU11P 0x4B49093 +#define FPGA_ID_XCVU13P 0x4B51093 +#define FPGA_ID_XCVU19P 0x4BA1093 +#define FPGA_ID_XCVU23P 0x4ACE093 +#define FPGA_ID_XCVU27P 0x4B43093 +#define FPGA_ID_XCVU29P 0x4B41093 +#define FPGA_ID_XCVU31P 0x4B6B093 +#define FPGA_ID_XCVU33P 0x4B69093 +#define FPGA_ID_XCVU35P 0x4B71093 +#define FPGA_ID_XCVU37P 0x4B79093 +#define FPGA_ID_XCVU45P 0x4B73093 +#define FPGA_ID_XCVU47P 0x4B7B093 +#define FPGA_ID_XCVU57P 0x4B61093 +// Zynq UltraScale+ +#define FPGA_ID_XCZU1 0x4688093 +#define FPGA_ID_XCZU2 0x4711093 +#define FPGA_ID_XCZU3 0x4710093 +#define FPGA_ID_XCZU4 0x4721093 +#define FPGA_ID_XCZU5 0x4720093 +#define FPGA_ID_XCZU6 0x4739093 +#define FPGA_ID_XCZU7 0x4730093 +#define FPGA_ID_XCZU9 0x4738093 +#define FPGA_ID_XCZU11 0x4740093 +#define FPGA_ID_XCZU15 0x4750093 +#define FPGA_ID_XCZU17 0x4759093 +#define FPGA_ID_XCZU19 0x4758093 +#define FPGA_ID_XCZU21 0x47E1093 +#define FPGA_ID_XCZU25 0x47E5093 +#define FPGA_ID_XCZU27 0x47E4093 +#define FPGA_ID_XCZU28 0x47E0093 +#define FPGA_ID_XCZU29 0x47E2093 +#define FPGA_ID_XCZU39 0x47E6093 +#define FPGA_ID_XCZU43 0x47FD093 +#define FPGA_ID_XCZU46 0x47F8093 +#define FPGA_ID_XCZU47 0x47FF093 +#define FPGA_ID_XCZU48 0x47FB093 +#define FPGA_ID_XCZU49 0x47FE093 +#define FPGA_ID_XCZU65 0x46D1093 +#define FPGA_ID_XCZU67 0x46D0093 +// Kria SoM (Zynq UltraScale+) +#define FPGA_ID_XCK26 0x4A49093 +// Alveo (Virtex UltraScale+) +#define FPGA_ID_XCU26_XCUX35 0x4AD5093 +#define FPGA_ID_XCU50_XCU55N 0x4B77093 +#define FPGA_ID_XCU200 0x4B37093 +#define FPGA_ID_XCU250 0x4B57093 +#define FPGA_ID_XCU280_XCU55C 0x4B7D093 +// Versal AI Edge +#define FPGA_ID_XCVE1752 0x4C9A093 +#define FPGA_ID_XCVE2002 0x4CC1093 +#define FPGA_ID_XCVE2102 0x4CC0093 +#define FPGA_ID_XCVE2202 0x4CC9093 +#define FPGA_ID_XCVE2302 0x4CC8093 +#define FPGA_ID_XCVE2602 0x4CD3093 +#define FPGA_ID_XCVE2802 0x4CD1093 +// Versal AI Core +#define FPGA_ID_XCVC1352 0x4C93093 +#define FPGA_ID_XCVC1502 0x4C9B093 +#define FPGA_ID_XCVC1702 0x4C98093 +#define FPGA_ID_XCVC1802 0x4CA9093 +#define FPGA_ID_XCVC1902 0x4CA8093 +#define FPGA_ID_XCVC2602 0x4CD2093 +#define FPGA_ID_XCVC2802 0x4CD0093 +// Versal Prime +#define FPGA_ID_XCVM1102 0x4CCA093 +#define FPGA_ID_XCVM1302 0x4C09093 +#define FPGA_ID_XCVM1402 0x4C08093 +#define FPGA_ID_XCVM1502 0x4C99093 +#define FPGA_ID_XCVM1802 0x4CAA093 +#define FPGA_ID_XCVM2202 0x4CD4093 +#define FPGA_ID_XCVM2302 0x4C24093 +#define FPGA_ID_XCVM2502 0x4D01093 +#define FPGA_ID_XCVM2902 0x4C23093 +// Versal Premium +#define FPGA_ID_XCVP1002 0x4C1B093 +#define FPGA_ID_XCVP1052 0x4C18093 +#define FPGA_ID_XCVP1102 0x4C22093 +#define FPGA_ID_XCVP1202 0x4D00093 +#define FPGA_ID_XCVP1402 0x4C20093 +#define FPGA_ID_XCVP1502 0x4D08093 +#define FPGA_ID_XCVP1552 0x4D34093 +#define FPGA_ID_XCVP1702 0x4D10093 +#define FPGA_ID_XCVP1802 0x4D14093 +#define FPGA_ID_XCVP2502 0x4D1C093 +#define FPGA_ID_XCVP2802 0x4D20093 + +// Intel +// Stratix 10 +#define FPGA_ID_1SG10MH_U1 0x032270DD +#define FPGA_ID_1SG10MH_U2 0x0322F0DD +#define FPGA_ID_1SG040H 0x032200DD +#define FPGA_ID_1SG040H_NL 0x032A00DD +#define FPGA_ID_1SG065H 0x032210DD +#define FPGA_ID_1SG065H_NL 0x032A10DD +#define FPGA_ID_1SG085H 0x132220DD +#define FPGA_ID_1SG110H 0x032220DD +#define FPGA_ID_1SG110H_NL 0x032A20DD +#define FPGA_ID_1SG165H 0xF32250DD +#define FPGA_ID_1SG166H 0x532240DD +#define FPGA_ID_1SG166H_NL 0x5322C0DD +#define FPGA_ID_1SG210H 0xE32250DD +#define FPGA_ID_1SG210H_ES1 0x232250DD +#define FPGA_ID_1SG211H 0x432240DD +#define FPGA_ID_1SG250L 0xD32150DD +#define FPGA_ID_1SG250H 0xD32250DD +#define FPGA_ID_1SG280L 0xC32150DD +#define FPGA_ID_1SG280L_NL 0xC32950DD +#define FPGA_ID_1SG280L_ES1 0x032150DD +#define FPGA_ID_1SG280L_ES2 0x032250DD +#define FPGA_ID_1SG280L_ES3 0xC32150DD +#define FPGA_ID_1SG280H 0xC32250DD +#define FPGA_ID_1SG280H_NL 0xC32A50DD +#define FPGA_ID_1SG280H_ES1 0x032250DD +#define FPGA_ID_1SG280H_ES2 0xC32150DD +#define FPGA_ID_1SG280H_ES3 0xC32250DD +#define FPGA_ID_1SX040H 0x032280DD +#define FPGA_ID_1SX065H 0x032290DD +#define FPGA_ID_1SX085H 0x1322A0DD +#define FPGA_ID_1SX110H 0x0322A0DD +#define FPGA_ID_1SX165H 0xF322D0DD +#define FPGA_ID_1SX210H 0xE322D0DD +#define FPGA_ID_1SX250L 0xD321D0DD +#define FPGA_ID_1SX250H 0xD322D0DD +#define FPGA_ID_1SX280L 0xC321D0DD +#define FPGA_ID_1SX280L_ES1 0x4321D0DD +#define FPGA_ID_1SX280L_ES2 0x4321D0DD +#define FPGA_ID_1SX280H 0xC322D0DD +#define FPGA_ID_1SX280H_ES1 0x4322D0DD +#define FPGA_ID_1SX280H_ES2 0xC322D0DD +#define FPGA_ID_1ST040E 0x032380DD +#define FPGA_ID_1ST040E_NL 0x032B00DD +#define FPGA_ID_1ST085E 0x1323A0DD +#define FPGA_ID_1ST110E 0x0323A0DD +#define FPGA_ID_1ST110E_NL 0x032B20DD +#define FPGA_ID_1ST165E 0x532340DD +#define FPGA_ID_1ST210E 0x432340DD +#define FPGA_ID_1ST210E_ES1 0x032340DD +#define FPGA_ID_1ST250E 0xD323D0DD +#define FPGA_ID_1ST280E 0xC323D0DD +#define FPGA_ID_1ST280E_ES1 0xC323D0DD +#define FPGA_ID_1SM16BE 0x732BC0DD +#define FPGA_ID_1SM16BE_ES1 0x732BC0DD +#define FPGA_ID_1SM16BH 0x732AC0DD +#define FPGA_ID_1SM16BH_ES1 0x332AC0DD +#define FPGA_ID_1SM16CH 0x532AC0DD +#define FPGA_ID_1SM16CH_ES1 0x132AC0DD +#define FPGA_ID_1SM21BE 0x632BC0DD +#define FPGA_ID_1SM21BE_ES1 0x632BC0DD +#define FPGA_ID_1SM21BH 0x632AC0DD +#define FPGA_ID_1SM21BH_ES1 0x232AC0DD +#define FPGA_ID_1SM21CH 0x432AC0DD +#define FPGA_ID_1SM21CH_ES1 0x032AC0DD +#define FPGA_ID_1SD110P 0x0324A0DD +#define FPGA_ID_1SD110P_NL 0x032C20DD +#define FPGA_ID_1SD21BP 0x632CC0DD +#define FPGA_ID_1SD280P 0xC32450DD +// Agilex +#define FPGA_ID_AGFA006R16A 0x134310DD +#define FPGA_ID_AGFA008R16A 0x034310DD +#define FPGA_ID_AGFA012R24A 0x534120DD +#define FPGA_ID_AGFA012R24B 0xD34120DD +#define FPGA_ID_AGFA014R24AR0 0x034120DD +#define FPGA_ID_AGFA014R24A 0x434120DD +#define FPGA_ID_AGFA014R24B 0xC34120DD +#define FPGA_ID_AGFA019R25A 0x134150DD +#define FPGA_ID_AGFA022R24C 0x134330DD +#define FPGA_ID_AGFA022R25A 0x134130DD +#define FPGA_ID_AGFA022R31C 0x134330DD +#define FPGA_ID_AGFA023R25AR0 0x034150DD +#define FPGA_ID_AGFA023R25A 0x034150DD +#define FPGA_ID_AGFA027R24CR0 0x034330DD +#define FPGA_ID_AGFA027R24CR2 0x034330DD +#define FPGA_ID_AGFA027R24C 0x034330DD +#define FPGA_ID_AGFA027R25AR0 0x034130DD +#define FPGA_ID_AGFA027R25A 0x034130DD +#define FPGA_ID_AGFA027R31C 0x034330DD +#define FPGA_ID_AGFB006R16A 0x134390DD +#define FPGA_ID_AGFB008R16A 0x034390DD +#define FPGA_ID_AGFB012R24A 0x5341A0DD +#define FPGA_ID_AGFB012R24B 0xD341A0DD +#define FPGA_ID_AGFB014R24AR0 0x0341A0DD +#define FPGA_ID_AGFB014R24A 0x4341A0DD +#define FPGA_ID_AGFB014R24B 0xC341A0DD +#define FPGA_ID_AGFB019R25A 0x1341D0DD +#define FPGA_ID_AGFB022R24C 0x1343B0DD +#define FPGA_ID_AGFB022R25A 0x1341B0DD +#define FPGA_ID_AGFB022R31C 0x1343B0DD +#define FPGA_ID_AGFB023R25AR0 0x0341D0DD +#define FPGA_ID_AGFB023R25A 0x0341D0DD +#define FPGA_ID_AGFB027R24CR0 0x0343B0DD +#define FPGA_ID_AGFB027R24CR2 0x0343B0DD +#define FPGA_ID_AGFB027R24C 0x0343B0DD +#define FPGA_ID_AGFB027R25AR0 0x0341B0DD +#define FPGA_ID_AGFB027R25A 0x0341B0DD +#define FPGA_ID_AGFB027R31C 0x0343B0DD +#define FPGA_ID_AGFC019R25A 0x334150DD +#define FPGA_ID_AGFC023R25AR0 0x234150DD +#define FPGA_ID_AGFC023R25A 0x234150DD +#define FPGA_ID_AGFD019R25A 0x3341D0DD +#define FPGA_ID_AGFD023R25AR0 0x2341D0DD +#define FPGA_ID_AGFD023R25A 0x2341D0DD +#define FPGA_ID_AGIB022R29A 0x134BB0DD +#define FPGA_ID_AGIB022R31B 0x1343B0DD +#define FPGA_ID_AGIB027R29AR0 0x034BB0DD +#define FPGA_ID_AGIB027R29AR1 0x034BB0DD +#define FPGA_ID_AGIB027R29AR3 0x034BB0DD +#define FPGA_ID_AGIB027R29A 0x034BB0DD +#define FPGA_ID_AGIB027R31BR0 0x0343B0DD +#define FPGA_ID_AGIB027R31B 0x0343B0DD + +const char *get_fpga_part(int id); + +#endif /* FPGA_ID_H */ diff --git a/src/pyrite/utils/pyrite.c b/src/pyrite/utils/pyrite.c new file mode 100644 index 0000000..3ce3d6b --- /dev/null +++ b/src/pyrite/utils/pyrite.c @@ -0,0 +1,1491 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fpga_id.h" +#include "reg_block.h" +#include "reg_if.h" +#include "bitfile.h" +#include "flash.h" + +#define MAX_SEGMENTS 8 + +uint32_t reverse_bits_32(uint32_t x) +{ + x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); + x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); + x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); + x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8); + x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16); + return x; +} + +uint16_t reverse_bits_16(uint16_t x) +{ + x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1); + x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2); + x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4); + x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8); + return x; +} + +uint8_t reverse_bits_8(uint8_t x) +{ + x = ((x & 0x55) << 1) | ((x & 0xAA) >> 1); + x = ((x & 0x33) << 2) | ((x & 0xCC) >> 2); + x = ((x & 0x0F) << 4) | ((x & 0xF0) >> 4); + return x; +} + +char* stristr(const char *str1, const char *str2) +{ + const char* p1 = str1; + const char* p2 = str2; + const char* r = *p2 == 0 ? str1 : 0; + + while (*p1 != 0 && *p2 != 0) { + if (tolower(*p1) == tolower(*p2)) { + if (r == 0) { + r = p1; + } + + p2++; + } else { + p2 = str2; + if (r != 0) { + p1 = r + 1; + } + + if (tolower(*p1) == tolower(*p2)) { + r = p1; + p2++; + } else { + r = 0; + } + } + + p1++; + } + + return *p2 == 0 ? (char *)r : 0; +} + +static void usage(char *name) +{ + fprintf(stderr, + "usage: %s [options]\n" + " -s domain:bus:dev.func select device\n" + " -p part select flash partition\n" + " -r file read flash to file\n" + " -w file write and verify flash from file\n" + " -e erase flash\n" + " -b boot FPGA from flash\n" + " -t hot reset FPGA\n" + " -y no interactive confirm\n", + name); +} + +int flash_read_progress(struct flash_device *fdev, size_t addr, size_t len, void *dest) +{ + int ret = 0; + size_t remain = len; + size_t seg; + int step = 0x10000; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + while (remain > 0) { + if (remain > step) { + // longer than step, trim + if ((addr + step) & (step-1)) { + // align to step size + seg = step - ((addr + step) & (step-1)); + } else { + // already aligned + seg = step; + } + } else { + // shorter than step + seg = remain; + } + + printf("Read address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, (100*(len-remain))/len); + fflush(stdout); + + ret = flash_read(fdev, addr, seg, dest); + + if (ret) { + fprintf(stderr, "\nRead failed\n"); + goto err; + } + + addr += seg; + remain -= seg; + dest += seg; + } + + printf("\n"); + +err: + return ret; +} + +int flash_write_progress(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + int ret = 0; + size_t remain = len; + size_t seg; + int step = 0x1000; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + step = fdev->write_buffer_size > step ? fdev->write_buffer_size : step; + + while (remain > 0) { + if (remain > step) { + // longer than step, trim + if ((addr + step) & (step-1)) { + // align to step size + seg = step - ((addr + step) & (step-1)); + } else { + // already aligned + seg = step; + } + } else { + // shorter than step + seg = remain; + } + + printf("Write address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, (100*(len-remain))/len); + fflush(stdout); + + ret = flash_write(fdev, addr, seg, src); + + if (ret) { + fprintf(stderr, "\nWrite failed\n"); + goto err; + } + + addr += seg; + remain -= seg; + src += seg; + } + + printf("\n"); + +err: + return ret; +} + +int flash_write_verify_progress(struct flash_device *fdev, size_t addr, size_t len, const void *src) +{ + int ret = 0; + size_t remain = len; + size_t seg; + int step = 0x1000; + const uint8_t *ptr = src; + uint8_t *check_buf; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + step = fdev->write_buffer_size > step ? fdev->write_buffer_size : step; + + check_buf = calloc(step, 1); + + if (!check_buf) + return -1; + + while (remain > 0) { + if (remain > step) { + // longer than step, trim + if ((addr + step) & (step-1)) { + // align to step size + seg = step - ((addr + step) & (step-1)); + } else { + // already aligned + seg = step; + } + } else { + // shorter than step + seg = remain; + } + + printf("Write/verify address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, (100*(len-remain))/len); + fflush(stdout); + + ret = flash_write(fdev, addr, seg, ptr); + + if (ret) { + fprintf(stderr, "\nWrite failed\n"); + goto err; + } + + for (int read_attempts = 3; read_attempts >= 0; read_attempts--) { + ret = flash_read(fdev, addr, seg, check_buf); + + if (ret) { + fprintf(stderr, "\nRead failed\n"); + goto err; + } + + if (memcmp(ptr, check_buf, seg)) { + fprintf(stderr, "\nVerify failed (%d more attempts)\n", read_attempts); + + for (size_t k = 0; k < seg; k++) { + if (ptr[k] != check_buf[k]) { + fprintf(stderr, "flash offset 0x%08lx: expected 0x%02x, read 0x%02x\n", + addr+k, ptr[k], check_buf[k]); + } + } + + if (read_attempts > 0) + continue; + + ret = -1; + goto err; + } + } + + addr += seg; + remain -= seg; + ptr += seg; + } + + printf("\n"); + +err: + free(check_buf); + return ret; +} + +int flash_erase_progress(struct flash_device *fdev, size_t addr, size_t len) +{ + int ret; + size_t remain = len; + size_t seg; + int step = 0x10000; + + printf("Start address: 0x%08lx\n", addr); + printf("Length: 0x%08lx\n", len); + + step = fdev->erase_block_size > step ? fdev->erase_block_size : step; + + while (remain > 0) { + if (remain > step) { + // longer than step, trim + if ((addr + step) & (step-1)) { + // align to step size + seg = step - ((addr + step) & (step-1)); + } else { + // already aligned + seg = step; + } + } else { + // shorter than step + seg = remain; + } + + printf("Erase address 0x%08lx, length 0x%08lx (%ld%%)\r", addr, seg, ((100*(len-remain))/len)); + fflush(stdout); + + ret = flash_erase(fdev, addr, seg); + + if (ret) + return ret; + + addr += seg; + remain -= seg; + } + + printf("\n"); + + return 0; +} + +int write_str_to_file(const char *file_name, const char *str) +{ + int ret = 0; + FILE *fp = fopen(file_name, "w"); + + if (!fp) { + perror("failed to open file"); + return -1; + } + + if (fputs(str, fp) == EOF) { + perror("failed to write to file"); + ret = -1; + } + + fclose(fp); + return ret; +} + +int write_1_to_file(const char *file_name) +{ + return write_str_to_file(file_name, "1"); +} + +#define FILE_TYPE_BIN 0 +#define FILE_TYPE_HEX 1 +#define FILE_TYPE_BIT 2 + +int file_type_from_ext(const char *file_name) +{ + char *ptr; + char buffer[32]; + + ptr = strrchr(file_name, '.'); + + if (!ptr) { + return FILE_TYPE_BIN; + } + + ptr++; + + for (int i = 0; i < sizeof(buffer)-1 && *ptr; i++) { + buffer[i] = tolower(*ptr++); + buffer[i+1] = 0; + } + + if (strcmp(buffer, "hex") == 0 || strcmp(buffer, "mcs") == 0) { + return FILE_TYPE_HEX; + } + + if (strcmp(buffer, "bit") == 0) { + return FILE_TYPE_BIT; + } + + return FILE_TYPE_BIN; +} + +int pcie_hot_reset(const char *pci_port_path) +{ + int fd; + char path[PATH_MAX+32]; + char buf[32]; + + snprintf(path, sizeof(path), "%s/config", pci_port_path); + + fd = open(path, O_RDWR); + + if (fd < 0) { + perror("Failed to open config region of port"); + return -1; + } + + // set and then clear secondary bus reset bit (mask 0x0040) + // in the bridge control register (offset 0x3e) + pread(fd, buf, 2, PCI_BRIDGE_CONTROL); + + buf[2] = buf[0] | PCI_BRIDGE_CTL_BUS_RESET; + buf[3] = buf[1]; + + pwrite(fd, buf+2, 2, PCI_BRIDGE_CONTROL); + + usleep(10000); + + pwrite(fd, buf, 2, PCI_BRIDGE_CONTROL); + + close(fd); + + return 0; +} + +int pcie_disable_fatal_err(const char *pci_port_path) +{ + int fd; + char path[PATH_MAX+32]; + char buf[32]; + int offset; + + snprintf(path, sizeof(path), "%s/config", pci_port_path); + + fd = open(path, O_RDWR); + + if (fd < 0) { + perror("Failed to open config region of port"); + return -1; + } + + // clear SERR bit (mask 0x0100) in command register (offset 0x04) + pread(fd, buf, 2, PCI_COMMAND); + + buf[1] &= ~(PCI_COMMAND_SERR >> 8); + + pwrite(fd, buf, 2, PCI_COMMAND); + + // clear fatal error reporting bit (mask 0x0004) in + // PCIe capability device control register (offset 0x08) + + // find PCIe capability (ID 0x10) + pread(fd, buf, 1, PCI_CAPABILITY_LIST); + + offset = buf[0] & 0xfc; + + while (offset > 0) { + pread(fd, buf, 2, offset); + + if (buf[0] == PCI_CAP_ID_EXP) + break; + + offset = buf[1] & 0xfc; + } + + // clear bit + if (offset) { + pread(fd, buf, 2, offset+PCI_EXP_DEVCTL); + + buf[0] &= ~PCI_EXP_DEVCTL_FERE; + + pwrite(fd, buf, 2, offset+PCI_EXP_DEVCTL); + } + + close(fd); + + return 0; +} + +struct vpd_reg_priv { + int fd; + int offset; +}; + +static int reg_if_vpd_read32(const struct reg_if *reg, size_t offset, uint32_t *value) +{ + const struct vpd_reg_priv *priv = reg->priv; + uint16_t offs = offset & 0x7fff; // clear F + pwrite(priv->fd, &offs, 2, priv->offset+PCI_VPD_ADDR); + pread(priv->fd, value, 4, priv->offset+PCI_VPD_DATA); + return 0; +} + +static int reg_if_vpd_write32(const struct reg_if *reg, size_t offset, uint32_t value) +{ + const struct vpd_reg_priv *priv = reg->priv; + uint16_t offs = offset | 0x8000; // set F + pwrite(priv->fd, &value, 4, priv->offset+PCI_VPD_DATA); + pwrite(priv->fd, &offs, 2, priv->offset+PCI_VPD_ADDR); + return 0; +} + +static void reg_if_vpd_close(const struct reg_if *reg) +{ + free(reg->priv); +} + +static const struct reg_if_ops reg_if_vpd_ops = { + .read32 = reg_if_vpd_read32, + .write32 = reg_if_vpd_write32, + .close = reg_if_vpd_close, +}; + +struct reg_if *reg_if_open_vpd(int fd) +{ + char buf[32]; + int offset; + + struct reg_if *reg = calloc(sizeof(struct reg_if), 1); + + if (!reg) + return NULL; + + struct vpd_reg_priv *priv = calloc(sizeof(struct vpd_reg_priv), 1); + + if (!priv) { + free(reg); + return NULL; + } + + reg->priv = priv; + reg->ops = ®_if_vpd_ops; + + // find VPD capability (ID 0x03) + pread(fd, buf, 1, PCI_CAPABILITY_LIST); + + offset = buf[0] & 0xfc; + + while (offset > 0) { + pread(fd, buf, 2, offset); + + if (buf[0] == PCI_CAP_ID_VPD) + break; + + offset = buf[1] & 0xfc; + } + + if (!offset || buf[0] != PCI_CAP_ID_VPD) { + perror("Failed to locate VPD capability"); + reg_if_close(reg); + return NULL; + } + + priv->fd = fd; + priv->offset = offset; + + return reg; +} + +int main(int argc, char *argv[]) +{ + char *name; + int opt; + int ret = 0; + + struct reg_if *vpd_regs; + + char dev_name[32] = ""; + char *read_file_name = NULL; + FILE *read_file = NULL; + char *write_file_name = NULL; + FILE *write_file = NULL; + + int config_fd; + + char path[PATH_MAX+32] = ""; + char pci_device_path[PATH_MAX] = ""; + char pci_port_path[PATH_MAX] = ""; + char *ptr; + + int slot = -1; + + char action_read = 0; + char action_write = 0; + char action_erase = 0; + char action_boot = 0; + char action_reset = 0; + char no_confirm = 0; + + struct reg_block *rb_list = NULL; + struct reg_block *fw_id_rb = NULL; + struct reg_block *flash_rb = NULL; + + uint32_t fpga_id; + uint32_t fw_id; + uint32_t fw_ver; + uint32_t board_id; + uint32_t board_ver; + uint32_t build_date; + uint32_t git_hash; + uint32_t rel_info; + const char *fpga_part; + + char build_date_str[32]; + + struct flash_device *pri_flash = NULL; + struct flash_device *sec_flash = NULL; + + int flash_segment_count = 0; + size_t flash_segment_start[MAX_SEGMENTS]; + size_t flash_segment_length[MAX_SEGMENTS]; + + printf("Pyrite flashing utility\n"); + printf("Version: 0.0.1\n"); + printf("Copyright (c) 2026 FPGA Ninja, LLC\n"); + printf("https://fpga.ninja/\n"); + + name = strrchr(argv[0], '/'); + name = name ? 1+name : argv[0]; + + while ((opt = getopt(argc, argv, "s:p:r:w:ebtyh?")) != EOF) { + switch (opt) { + case 's': + { + int domain; + int bus; + int dev; + int func; + + if (sscanf(optarg, "%x:%x:%x.%x", &domain, &bus, &dev, &func) == 4) { + + } else if (sscanf(optarg, "%x:%x.%x", &bus, &dev, &func) == 3) { + domain = 0; + } else { + fprintf(stderr, "Failed to parse PCIe device ID\n"); + usage(name); + return -1; + } + + snprintf(dev_name, sizeof(dev_name), "%04x:%02x:%02x.%x", domain, bus, dev, func); + } + + break; + case 'p': + slot = atoi(optarg); + break; + case 'r': + action_read = 1; + read_file_name = optarg; + break; + case 'w': + action_write = 1; + write_file_name = optarg; + break; + case 'e': + action_erase = 1; + break; + case 'b': + action_boot = 1; + action_reset = 1; + break; + case 't': + action_reset = 1; + break; + case 'y': + no_confirm = 1; + break; + case 'h': + case '?': + usage(name); + return 0; + default: + usage(name); + return -1; + } + } + + if (strlen(dev_name) == 0) { + fprintf(stderr, "Device not specified\n"); + usage(name); + return -1; + } + + snprintf(pci_device_path, sizeof(pci_device_path), "/sys/bus/pci/devices/%s", dev_name); + + snprintf(path, sizeof(path), "%s/config", pci_device_path); + + config_fd = open(path, O_RDWR); + + if (config_fd < 0) { + perror("Failed to open config region"); + return -1; + } + + vpd_regs = reg_if_open_vpd(config_fd); + if (!vpd_regs) { + perror("Failed to initialize VPD capability"); + ret = -1; + goto err; + } + + // determine sysfs path of upstream port + realpath(pci_device_path, pci_port_path); + ptr = strrchr(pci_port_path, '/'); + if (ptr) + *ptr = 0; + + printf("PCIe ID (device): %s\n", strrchr(pci_device_path, '/')+1); + printf("PCIe ID (upstream port): %s\n", strrchr(pci_port_path, '/')+1); + + rb_list = enumerate_reg_block_list(vpd_regs, 0x4000, 0, 0x4000); + + printf("Register blocks:\n"); + for (struct reg_block *rb = rb_list; rb->regs; rb++) + printf(" type 0x%08x (v %d.%d.%d)\n", rb->type, rb->version >> 20, + (rb->version >> 12) & 0xff, rb->version & 0xfff); + + fw_id_rb = find_reg_block(rb_list, 0xffffffff, 0, 0); + if (!fw_id_rb) { + perror("Failed to find firmware ID block"); + ret = -1; + goto err; + } + + reg_if_read32(fw_id_rb->regs, 0x0C, &fpga_id); + reg_if_read32(fw_id_rb->regs, 0x10, &fw_id); + reg_if_read32(fw_id_rb->regs, 0x14, &fw_ver); + reg_if_read32(fw_id_rb->regs, 0x18, &board_id); + reg_if_read32(fw_id_rb->regs, 0x1C, &board_ver); + reg_if_read32(fw_id_rb->regs, 0x20, &build_date); + reg_if_read32(fw_id_rb->regs, 0x24, &git_hash); + reg_if_read32(fw_id_rb->regs, 0x28, &rel_info); + + fpga_part = get_fpga_part(fpga_id); + + time_t build_date_time = build_date; + struct tm *tm_info = gmtime(&build_date_time); + strftime(build_date_str, sizeof(build_date_str), "%F %T", tm_info); + + printf("FPGA ID: 0x%08x\n", fpga_id); + printf("FPGA part: %s\n", fpga_part); + printf("FW ID: 0x%08x\n", fw_id); + printf("FW version: %d.%d.%d\n", fw_ver >> 20, + (fw_ver >> 12) & 0xff, + fw_ver & 0xfff); + printf("Board ID: 0x%08x\n", board_id); + printf("Board version: %d.%d.%d\n", board_ver >> 20, + (board_ver >> 12) & 0xff, + board_ver & 0xfff); + printf("Build date: %s UTC (raw 0x%08x)\n", build_date_str, build_date); + printf("Git hash: %08x\n", git_hash); + printf("Release info: %08x\n", rel_info); + + if (fpga_id == 0 || fpga_id == 0xffffffff) { + fprintf(stderr, "Invalid FPGA ID\n"); + ret = -1; + goto skip_flash; + } + + uint32_t flash_format = 0; + + uint8_t flash_configuration = 0; + uint8_t flash_data_width = 0; + uint8_t flash_default_segment = 0; + uint8_t flash_fallback_segment = 0; + uint32_t flash_segment0_length = 0; + + int bitswap = 0; + int word_size = 8; + int dual_qspi = 0; + + size_t flash_size = 0; + size_t segment_size = 0; + size_t segment_offset = 0; + + if ((flash_rb = find_reg_block(rb_list, 0x0000c120, 0, 0))) { + uint32_t reg_val; + + // SPI flash + reg_if_read32(flash_rb->regs, 0x0C, &flash_format); + + printf("Flash type: SPI\n"); + printf("Flash format: 0x%08x\n", flash_format); + + switch (flash_rb->version) { + case 0x00000100: + case 0x00001000: + flash_configuration = flash_format & 0xf; + flash_default_segment = (flash_format >> 4) & 0xf; + flash_fallback_segment = (flash_format >> 8) & 0xf; + flash_segment0_length = flash_format & 0xfffff000; + break; + default: + fprintf(stderr, "Unknown SPI flash block version\n"); + ret = -1; + goto skip_flash; + } + + // determine data width + flash_data_width = 0; + + reg_if_write32(flash_rb->regs, 0x10, 0x00020f0f); + reg_if_read32(flash_rb->regs, 0x10, ®_val); + reg_if_write32(flash_rb->regs, 0x10, 0x00020000); + + reg_val &= 0xf; + + while (reg_val) { + reg_val >>= 1; + flash_data_width++; + } + + reg_if_write32(flash_rb->regs, 0x14, 0x00020f0f); + reg_if_read32(flash_rb->regs, 0x14, ®_val); + reg_if_write32(flash_rb->regs, 0x14, 0x00020000); + + reg_val &= 0xf; + + while (reg_val) { + reg_val >>= 1; + flash_data_width++; + } + + printf("Data width: %d\n", flash_data_width); + + if (flash_data_width > 4) { + dual_qspi = 1; + pri_flash = flash_open_spi(4, flash_rb->regs, 0x10); + sec_flash = flash_open_spi(4, flash_rb->regs, 0x14); + + if (!pri_flash || !sec_flash) { + fprintf(stderr, "Failed to connect to flash device\n"); + ret = -1; + goto skip_flash; + } + + flash_size = pri_flash->size+sec_flash->size; + } else { + pri_flash = flash_open_spi(4, flash_rb->regs, 0x10); + + if (!pri_flash) { + fprintf(stderr, "Failed to connect to flash device\n"); + ret = -1; + goto skip_flash; + } + + flash_size = pri_flash->size; + } + } else if ((flash_rb = find_reg_block(rb_list, 0x0000c121, 0, 0))) { + uint32_t reg_val; + + // BPI flash + reg_if_read32(flash_rb->regs, 0x0C, &flash_format); + + printf("Flash type: BPI\n"); + printf("Flash format: 0x%08x\n", flash_format); + + switch (flash_rb->version) { + case 0x00000100: + case 0x00001000: + flash_configuration = flash_format & 0xf; + flash_default_segment = (flash_format >> 4) & 0xf; + flash_fallback_segment = (flash_format >> 8) & 0xf; + flash_segment0_length = flash_format & 0xfffff000; + break; + default: + fprintf(stderr, "Unknown BPI flash block version\n"); + ret = -1; + goto skip_flash; + } + + // determine data width + reg_if_write32(flash_rb->regs, 0x10, 0x0001010f); + reg_if_write32(flash_rb->regs, 0x18, 0xffffffff); + reg_if_read32(flash_rb->regs, 0x18, ®_val); + reg_if_write32(flash_rb->regs, 0x10, 0x0000000f); + reg_if_write32(flash_rb->regs, 0x18, 0x00000000); + + flash_data_width = 0; + while (reg_val) { + reg_val >>= 1; + flash_data_width++; + } + + printf("Data width: %d\n", flash_data_width); + + bitswap = 1; + + if (flash_data_width == 16) { + word_size = 16; + } + + pri_flash = flash_open_bpi(flash_data_width, + flash_rb->regs, 0x10, 0x14, 0x18); + + if (!pri_flash) { + fprintf(stderr, "Failed to connect to flash device\n"); + ret = -1; + goto skip_flash; + } + + flash_size = pri_flash->size; + } else { + fprintf(stderr, "Failed to detect flash\n"); + ret = -1; + goto skip_flash; + } + + switch (flash_configuration) { + case 0: + case 1: + flash_segment_count = 1; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size; + break; + case 2: + if (flash_segment0_length == 0) { + flash_segment0_length = flash_size >> 1; + } else if (flash_size < flash_segment0_length) { + fprintf(stderr, "Invalid flash configuration\n"); + ret = -1; + goto skip_flash; + } + + flash_segment_count = 2; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_segment0_length; + flash_segment_start[1] = flash_segment_start[0]+flash_segment_length[0]; + flash_segment_length[1] = flash_size-flash_segment_start[1]; + break; + case 4: + flash_segment_count = 4; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size >> 2; + for (int k = 1; k < 4; k++) { + flash_segment_start[k] = flash_segment_start[k-1]+flash_segment_length[k-1]; + flash_segment_length[k] = flash_size >> 2; + } + break; + case 8: + flash_segment_count = 8; + flash_segment_start[0] = 0; + flash_segment_length[0] = flash_size >> 3; + for (int k = 1; k < 8; k++) { + flash_segment_start[k] = flash_segment_start[k-1]+flash_segment_length[k-1]; + flash_segment_length[k] = flash_size >> 3; + } + break; + default: + fprintf(stderr, "Unknown flash configuration (0x%02x)\n", flash_configuration); + ret = -1; + goto skip_flash; + } + + for (int k = 0; k < flash_segment_count; k++) { + printf("Flash segment %d: start 0x%08lx length 0x%08lx\n", k, flash_segment_start[k], flash_segment_length[k]); + } + + printf("Default segment: %d\n", flash_default_segment); + if (flash_fallback_segment == flash_default_segment || flash_fallback_segment >= flash_segment_count) { + printf("Fallback segment: none\n"); + } else { + printf("Fallback segment: %d\n", flash_fallback_segment); + } + + if (slot < 0) { + slot = flash_default_segment; + } + + if ((action_read || action_write) && (slot < 0 || slot >= flash_segment_count)) { + fprintf(stderr, "Requested slot is not valid (%d)\n", slot); + ret = -1; + goto err; + } + + segment_offset = flash_segment_start[slot]; + segment_size = flash_segment_length[slot]; + + printf("Selected: segment %d start 0x%08lx length 0x%08lx\n", slot, segment_offset, segment_size); + + if (action_erase) { + if (!no_confirm) { + char str[32]; + + printf("Are you sure you want to erase the selected segment?\n"); + printf("[y/N]: "); + + fgets(str, sizeof(str), stdin); + + if (str[0] != 'y' && str[0] != 'Y') + goto err; + } + + if (dual_qspi) { + // Dual QSPI flash + printf("Erasing primary flash...\n"); + if (flash_erase_progress(pri_flash, segment_offset/2, segment_size/2)) { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + goto err; + } + + printf("Erasing secondary flash...\n"); + if (flash_erase_progress(sec_flash, segment_offset/2, segment_size/2)) { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + goto err; + } + + printf("Erase complete!\n"); + } else { + // SPI or BPI flash + printf("Erasing flash...\n"); + if (flash_erase_progress(pri_flash, segment_offset, segment_size)) { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + goto err; + } + + printf("Erase complete!\n"); + } + } + + if (action_write) { + char *segment = calloc(segment_size, 1); + memset(segment, 0xff, segment_size); + size_t len; + + int file_type = file_type_from_ext(write_file_name); + + if (file_type == FILE_TYPE_BIN) { + // read binary file + printf("Reading binary file \"%s\"...\n", write_file_name); + write_file = fopen(write_file_name, "rb"); + + if (!write_file) { + fprintf(stderr, "Failed to open file\n"); + free(segment); + ret = -1; + goto err; + } + + fseek(write_file, 0, SEEK_END); + len = ftell(write_file); + rewind(write_file); + + if (len > segment_size) { + fprintf(stderr, "File larger than segment (%ld > %ld)\n", len, segment_size); + fclose(write_file); + free(segment); + ret = -1; + goto err; + } + + if (fread(segment, 1, len, write_file) < len) { + fprintf(stderr, "Error reading file\n"); + fclose(write_file); + free(segment); + ret = -1; + goto err; + } + + fclose(write_file); + } else if (file_type == FILE_TYPE_BIT) { + // read bit file + struct bitfile *bf; + char part[128]; + char *ptr1, *ptr2; + int match = 0; + + printf("Reading bit file \"%s\"...\n", write_file_name); + bf = bitfile_create_from_file(write_file_name); + + if (!bf) { + fprintf(stderr, "Error reading bit file\n"); + free(segment); + ret = -1; + goto err; + } + + printf("Part: %s\n", bf->part); + printf("Date: %s %s\n", bf->date, bf->time); + + // check device type + // fpga_part may contain multiple possible device types, separated by underscores + strcpy(part, fpga_part); + ptr1 = ptr2 = part; + + while (ptr2) + { + ptr2 = strchr(ptr1, '_'); + + if (ptr2) + *ptr2 = 0; + + if (stristr(bf->part, ptr1) == bf->part) + match = 1; + + if (ptr2) + ptr1 = ptr2+1; + } + + if (!match) { + fprintf(stderr, "Device mismatch (target is %s, file is %s)\n", fpga_part, bf->part); + bitfile_close(bf); + free(segment); + ret = -1; + goto err; + } + + // check for available space + if (bf->data_len > segment_size) { + fprintf(stderr, "File larger than segment (%ld > %ld)\n", bf->data_len, segment_size); + bitfile_close(bf); + free(segment); + ret = -1; + goto err; + } + + len = bf->data_len; + memcpy(segment, bf->data, bf->data_len); + + bitfile_close(bf); + } else if (file_type == FILE_TYPE_HEX) { + fprintf(stderr, "Hex files are not currently supported\n"); + free(segment); + ret = -1; + goto err; + } else { + fprintf(stderr, "Unsupported file type\n"); + free(segment); + ret = -1; + goto err; + } + + // check sync word + if (memcmp(segment+0x50, "\xAA\x99\x55\x66", 4)) { + fprintf(stderr, "Bitstream sync word not found\n"); + free(segment); + ret = -1; + goto err; + } + + // TODO check for and confirm FPGA ID + + if (bitswap) + { + if (word_size == 16) + { + uint16_t *p = (uint16_t *)segment; + + for (size_t k = 0; k < segment_size; k += 2) { + *p = reverse_bits_16(*p); + p++; + } + } else { + uint8_t *p = (uint8_t *)segment; + + for (size_t k = 0; k < segment_size; k++) { + *p = reverse_bits_8(*p); + p++; + } + } + } + + if (dual_qspi) { + // Dual QSPI flash + + // check sync word for dual QSPI re-sync + if (memcmp(segment+0x70, "\xAA\x99\x55\x66", 4)) { + fprintf(stderr, "Bitstream sync word not found for dual QSPI re-sync\n"); + free(segment); + ret = -1; + goto err; + } + + char *pri_buf = calloc(segment_size/2, 1); + char *sec_buf = calloc(segment_size/2, 1); + memset(pri_buf, 0xff, segment_size/2); + memset(sec_buf, 0xff, segment_size/2); + + int offset = 0x68; + + size_t len_int = (len - offset) / 2 + offset; + + if (len_int > segment_size/2) + len_int = segment_size/2; + + memcpy(pri_buf, segment, offset); + + char *c1 = pri_buf+offset; + char *c2 = sec_buf+offset; + + for (size_t k = offset; k < segment_size-offset; k += 2) { + *c1 = (segment[k+1] & 0x0f) | ((segment[k] << 4) & 0xf0); + *c2 = ((segment[k+1] >> 4) & 0x0f) | (segment[k] & 0xf0); + c1++; + c2++; + } + + // round up length to block size + if ((segment_offset/2 + len_int) & (pri_flash->erase_block_size-1)) { + len_int += pri_flash->erase_block_size - ((segment_offset/2 + len_int) & (pri_flash->erase_block_size-1)); + } + + if (!no_confirm) { + char str[32]; + + printf("Are you sure you want to write the selected segment?\n"); + printf("[y/N]: "); + + fgets(str, sizeof(str), stdin); + + if (str[0] != 'y' && str[0] != 'Y') + goto err; + } + + printf("Erasing primary flash...\n"); + if (flash_erase_progress(pri_flash, segment_offset/2, len_int)) { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Erasing secondary flash...\n"); + if (flash_erase_progress(sec_flash, segment_offset/2, len_int)) { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Writing and verifying primary flash...\n"); + if (flash_write_verify_progress(pri_flash, segment_offset/2, len_int, pri_buf)) { + fprintf(stderr, "Write/verify failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Writing and verifying secondary flash...\n"); + if (flash_write_verify_progress(sec_flash, segment_offset/2, len_int, sec_buf)) { + fprintf(stderr, "Write/verify failed!\n"); + ret = -1; + free(segment); + free(pri_buf); + free(sec_buf); + goto err; + } + + printf("Programming succeeded!\n"); + + free(pri_buf); + free(sec_buf); + } else { + // SPI or BPI flash + + // round up length to block size + if ((segment_offset + len) & (pri_flash->erase_block_size-1)) { + len += pri_flash->erase_block_size - ((segment_offset + len) & (pri_flash->erase_block_size-1)); + } + + if (!no_confirm) { + char str[32]; + + printf("Are you sure you want to write the selected segment?\n"); + printf("[y/N]: "); + + fgets(str, sizeof(str), stdin); + + if (str[0] != 'y' && str[0] != 'Y') + goto err; + } + + printf("Erasing flash...\n"); + if (flash_erase_progress(pri_flash, segment_offset, len)) { + fprintf(stderr, "Erase failed!\n"); + ret = -1; + free(segment); + goto err; + } + + printf("Writing and verifying flash...\n"); + if (flash_write_verify_progress(pri_flash, segment_offset, len, segment)) { + fprintf(stderr, "Write/verify failed!\n"); + ret = -1; + free(segment); + goto err; + } + + printf("Programming succeeded!\n"); + } + + free(segment); + } + + if (action_read) { + char *segment = calloc(segment_size, 1); + memset(segment, 0xff, segment_size); + + if (dual_qspi) { + char *pri_buf = calloc(segment_size/2, 1); + char *sec_buf = calloc(segment_size/2, 1); + + printf("Reading primary flash...\n"); + flash_read_progress(pri_flash, segment_offset/2, segment_size/2, pri_buf); + printf("Reading secondary flash...\n"); + flash_read_progress(sec_flash, segment_offset/2, segment_size/2, sec_buf); + + int offset = 0x68; + + memcpy(segment, pri_buf, offset); + + char *c1 = pri_buf+offset; + char *c2 = sec_buf+offset; + + for (size_t k = offset; k < segment_size-offset; k += 2) { + segment[k] = ((*c1 >> 4) & 0x0f) | (*c2 & 0xf0); + segment[k+1] = (*c1 & 0x0f) | ((*c2 << 4) & 0xf0); + c1++; + c2++; + } + + free(pri_buf); + free(sec_buf); + } else { + printf("Reading flash...\n"); + flash_read_progress(pri_flash, segment_offset, segment_size, segment); + } + + if (bitswap) { + if (word_size == 16) { + uint16_t *p = (uint16_t *)segment; + + for (size_t k = 0; k < segment_size; k += 2) { + *p = reverse_bits_16(*p); + p++; + } + } else { + uint8_t *p = (uint8_t *)segment; + + for (size_t k = 0; k < segment_size; k++) { + *p = reverse_bits_8(*p); + p++; + } + } + } + + int file_type = file_type_from_ext(read_file_name); + + if (file_type == FILE_TYPE_BIN) { + // write binary file + printf("Writing binary file \"%s\"...\n", read_file_name); + read_file = fopen(read_file_name, "wb"); + fwrite(segment, 1, segment_size, read_file); + fclose(read_file); + } else if (file_type == FILE_TYPE_HEX) { + fprintf(stderr, "Hex files are not currently supported\n"); + free(segment); + ret = -1; + goto err; + } else { + fprintf(stderr, "Unsupported file type\n"); + free(segment); + ret = -1; + goto err; + } + + free(segment); + } + +skip_flash: + if (ret && (action_read || action_write)) { + goto err; + } else { + ret = 0; + } + + flash_release(pri_flash); + pri_flash = NULL; + flash_release(sec_flash); + sec_flash = NULL; + + if (action_boot || action_reset) { + if (!no_confirm) { + char str[32]; + + if (action_boot) + printf("Are you sure you want to boot from flash?\n"); + else + printf("Are you sure you want to perform a reset?\n"); + printf("[y/N]: "); + + fgets(str, sizeof(str), stdin); + + if (str[0] != 'y' && str[0] != 'Y') + goto err; + } + + printf("Preparing to reset device...\n"); + + // disable fatal error reporting on port (to prevent IPMI-triggered reboot) + printf("Disabling PCIe fatal error reporting on port...\n"); + pcie_disable_fatal_err(pci_port_path); + + // attempt to disconnect driver + snprintf(path, sizeof(path), "%s/driver/unbind", pci_device_path); + + if (access(path, F_OK) == 0) { + printf("Unbinding driver...\n"); + write_str_to_file(path, ptr+1); + } else { + printf("No driver bound\n"); + } + + sleep(1); + + // trigger FPGA reload + if (action_boot) { + // reload FPGA + printf("Triggering IPROG to reload FPGA...\n"); + if (flash_rb) + reg_if_write32(flash_rb->regs, 0x0C, 0xFEE1DEAD); + reg_if_write32(fw_id_rb->regs, 0x0C, 0xFEE1DEAD); + + // disconnect + reg_if_close(vpd_regs); + vpd_regs = NULL; + close(config_fd); + } + + // remove PCIe device + printf("Removing device...\n"); + + snprintf(path, sizeof(path), "%s/remove", pci_device_path); + + if (write_1_to_file(path)) { + fprintf(stderr, "Failed to remove device!\n"); + ret = -1; + goto err; + } + + if (action_boot) { + // give FPGA some time to boot from flash + sleep(4); + } + + sleep(1); + + for (int tries = 5; tries > 0; tries--) { + printf("Performing hot reset on upstream port...\n"); + pcie_hot_reset(pci_port_path); + + sleep(2); + + printf("Rescanning on upstream port...\n"); + + snprintf(path, sizeof(path), "%s/rescan", pci_port_path); + + if (write_1_to_file(path)) { + fprintf(stderr, "Rescan failed!\n"); + ret = -1; + goto err; + } + + // PCIe device will have a config space, so check for that + snprintf(path, sizeof(path), "%s/config", pci_device_path); + + if (access(path, F_OK) == 0) { + printf("Success, device is online!\n"); + break; + } else { + if (tries > 0) { + printf("Rescan failed, attempting another reset (up to %d more)\n", tries); + } else { + fprintf(stderr, "Rescan failed, device is offline!\n"); + ret = -1; + goto err; + } + } + } + + } + +err: + flash_release(pri_flash); + flash_release(sec_flash); + + reg_if_close(vpd_regs); + close(config_fd); + + return ret; +} diff --git a/src/pyrite/utils/reg_block.c b/src/pyrite/utils/reg_block.c new file mode 100644 index 0000000..f8d0444 --- /dev/null +++ b/src/pyrite/utils/reg_block.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include "reg_block.h" + +#include +#include + +struct reg_block *enumerate_reg_block_list(struct reg_if *regs, size_t base, size_t offset, size_t size) +{ + int max_count = 8; + struct reg_block *reg_block_list = calloc(max_count, sizeof(struct reg_block)); + int count = 0; + + size_t ptr; + + uint32_t rb_type; + uint32_t rb_version; + uint32_t val; + + if (!reg_block_list) + return NULL; + + while (1) { + reg_block_list[count].type = 0; + reg_block_list[count].version = 0; + reg_block_list[count].regs = NULL; + + if ((offset == 0 && count != 0) || offset >= size) + break; + + ptr = base + offset; + + for (int k = 0; k < count; k++) { + if (ptr == reg_block_list[k].offset) { + fprintf(stderr, "Register blocks form a loop\n"); + goto fail; + } + } + + reg_if_read32(regs, ptr+0x00, &rb_type); + reg_if_read32(regs, ptr+0x04, &rb_version); + reg_if_read32(regs, ptr+0x08, &val); + + reg_block_list[count].type = rb_type; + reg_block_list[count].version = rb_version; + reg_block_list[count].offset = ptr; + reg_block_list[count].regs = reg_if_open_offset(regs, ptr, size-offset); + + offset = val; + + count++; + + if (count >= max_count) { + struct reg_block *tmp; + max_count += 4; + tmp = realloc(reg_block_list, max_count * sizeof(struct reg_block)); + if (!tmp) + goto fail; + reg_block_list = tmp; + } + } + + return reg_block_list; +fail: + free_reg_block_list(reg_block_list); + return NULL; +} + +struct reg_block *find_reg_block(struct reg_block *list, uint32_t type, uint32_t version, int index) +{ + struct reg_block *rb = list; + + while (rb->regs) { + if (rb->type == type && (!version || rb->version == version)) { + if (index > 0) { + index--; + } else { + return rb; + } + } + + rb++; + } + + return NULL; +} + +void free_reg_block_list(struct reg_block *list) +{ + struct reg_block *rb = list; + + while (rb->regs) { + reg_if_close(rb->regs); + rb->regs = NULL; + + rb++; + } + + free(list); +} diff --git a/src/pyrite/utils/reg_block.h b/src/pyrite/utils/reg_block.h new file mode 100644 index 0000000..363de7c --- /dev/null +++ b/src/pyrite/utils/reg_block.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL */ +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#ifndef REG_BLOCK_H +#define REG_BLOCK_H + +#include +#include + +#include "reg_if.h" + +struct reg_block { + uint32_t type; + uint32_t version; + size_t offset; + struct reg_if *regs; +}; + +struct reg_block *enumerate_reg_block_list(struct reg_if *regs, size_t base, size_t offset, size_t size); +struct reg_block *find_reg_block(struct reg_block *list, uint32_t type, uint32_t version, int index); +void free_reg_block_list(struct reg_block *list); + +#endif /* REG_BLOCK_H */ diff --git a/src/pyrite/utils/reg_if.c b/src/pyrite/utils/reg_if.c new file mode 100644 index 0000000..4d084e3 --- /dev/null +++ b/src/pyrite/utils/reg_if.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#include + +#include "reg_if.h" + +int reg_if_read8(const struct reg_if *reg, size_t offset, uint8_t *value) +{ + if (!reg || !reg->ops || !reg->ops->read8) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->read8(reg, offset, value); +} + +int reg_if_write8(const struct reg_if *reg, size_t offset, uint8_t value) +{ + if (!reg || !reg->ops || !reg->ops->write8) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->write8(reg, offset, value); +} + +int reg_if_read16(const struct reg_if *reg, size_t offset, uint16_t *value) +{ + if (!reg || !reg->ops || !reg->ops->read16) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->read16(reg, offset, value); +} + +int reg_if_write16(const struct reg_if *reg, size_t offset, uint16_t value) +{ + if (!reg || !reg->ops || !reg->ops->write16) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->write16(reg, offset, value); +} + +int reg_if_read32(const struct reg_if *reg, size_t offset, uint32_t *value) +{ + if (!reg || !reg->ops || !reg->ops->read32) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->read32(reg, offset, value); +} + +int reg_if_write32(const struct reg_if *reg, size_t offset, uint32_t value) +{ + if (!reg || !reg->ops || !reg->ops->write32) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->write32(reg, offset, value); +} + +int reg_if_read64(const struct reg_if *reg, size_t offset, uint64_t *value) +{ + if (!reg || !reg->ops || !reg->ops->read64) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->read64(reg, offset, value); +} + +int reg_if_write64(const struct reg_if *reg, size_t offset, uint64_t value) +{ + if (!reg || !reg->ops || !reg->ops->write64) + return -1; + if (reg->size && offset >= reg->size) + return -1; + return reg->ops->write64(reg, offset, value); +} + +void reg_if_close(struct reg_if *reg) +{ + if (!reg) + return; + if (reg->ops && reg->ops->close) + reg->ops->close(reg); + free(reg); +} + +static int reg_if_raw_read8(const struct reg_if *reg, size_t offset, uint8_t *value) +{ + uint8_t *regs = reg->priv; + *value = *(volatile uint8_t *)(regs+offset); + return 0; +} + +static int reg_if_raw_write8(const struct reg_if *reg, size_t offset, uint8_t value) +{ + uint8_t *regs = reg->priv; + *(volatile uint8_t *)(regs+offset) = value; + return 0; +} + +static int reg_if_raw_read16(const struct reg_if *reg, size_t offset, uint16_t *value) +{ + uint8_t *regs = reg->priv; + *value = *(volatile uint16_t *)(regs+offset); + return 0; +} + +static int reg_if_raw_write16(const struct reg_if *reg, size_t offset, uint16_t value) +{ + uint8_t *regs = reg->priv; + *(volatile uint16_t *)(regs+offset) = value; + return 0; +} + +static int reg_if_raw_read32(const struct reg_if *reg, size_t offset, uint32_t *value) +{ + uint8_t *regs = reg->priv; + *value = *(volatile uint32_t *)(regs+offset); + return 0; +} + +static int reg_if_raw_write32(const struct reg_if *reg, size_t offset, uint32_t value) +{ + uint8_t *regs = reg->priv; + *(volatile uint32_t *)(regs+offset) = value; + return 0; +} + +static int reg_if_raw_read64(const struct reg_if *reg, size_t offset, uint64_t *value) +{ + uint8_t *regs = reg->priv; + *value = *(volatile uint64_t *)(regs+offset); + return 0; +} + +static int reg_if_raw_write64(const struct reg_if *reg, size_t offset, uint64_t value) +{ + uint8_t *regs = reg->priv; + *(volatile uint64_t *)(regs+offset) = value; + return 0; +} + +static const struct reg_if_ops reg_if_raw_ops = { + .read8 = reg_if_raw_read8, + .write8 = reg_if_raw_write8, + .read16 = reg_if_raw_read16, + .write16 = reg_if_raw_write16, + .read32 = reg_if_raw_read32, + .write32 = reg_if_raw_write32, + .read64 = reg_if_raw_read64, + .write64 = reg_if_raw_write64, +}; + +struct reg_if *reg_if_open_raw(void *regs, size_t size) +{ + struct reg_if *reg = calloc(sizeof(struct reg_if), 1); + + if (!reg) + return NULL; + + reg->ops = ®_if_raw_ops; + reg->priv = regs; + reg->size = size; + reg->offset = 0; + + return reg; +} + +static int reg_if_offset_read8(const struct reg_if *reg, size_t offset, uint8_t *value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_read8(regs, reg->offset+offset, value); +} + +static int reg_if_offset_write8(const struct reg_if *reg, size_t offset, uint8_t value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_write8(regs, reg->offset+offset, value); +} + +static int reg_if_offset_read16(const struct reg_if *reg, size_t offset, uint16_t *value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_read16(regs, reg->offset+offset, value); +} + +static int reg_if_offset_write16(const struct reg_if *reg, size_t offset, uint16_t value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_write16(regs, reg->offset+offset, value); +} + +static int reg_if_offset_read32(const struct reg_if *reg, size_t offset, uint32_t *value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_read32(regs, reg->offset+offset, value); +} + +static int reg_if_offset_write32(const struct reg_if *reg, size_t offset, uint32_t value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_write32(regs, reg->offset+offset, value); +} + +static int reg_if_offset_read64(const struct reg_if *reg, size_t offset, uint64_t *value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_read64(regs, reg->offset+offset, value); +} + +static int reg_if_offset_write64(const struct reg_if *reg, size_t offset, uint64_t value) +{ + const struct reg_if *regs = reg->priv; + return reg_if_write64(regs, reg->offset+offset, value); +} + +static const struct reg_if_ops reg_if_offset_ops = { + .read8 = reg_if_offset_read8, + .write8 = reg_if_offset_write8, + .read16 = reg_if_offset_read16, + .write16 = reg_if_offset_write16, + .read32 = reg_if_offset_read32, + .write32 = reg_if_offset_write32, + .read64 = reg_if_offset_read64, + .write64 = reg_if_offset_write64, +}; + +struct reg_if *reg_if_open_offset(struct reg_if *regs, size_t offset, size_t size) +{ + if (!regs) + return NULL; + + if (regs->size && offset >= regs->size) + return NULL; + + if (regs->size && (!size || size >= regs->size)) + size = regs->size - offset; + + struct reg_if *reg = calloc(sizeof(struct reg_if), 1); + + if (!reg) + return NULL; + + reg->ops = ®_if_offset_ops; + reg->priv = regs; + reg->size = size; + reg->offset = offset; + + return reg; +} diff --git a/src/pyrite/utils/reg_if.h b/src/pyrite/utils/reg_if.h new file mode 100644 index 0000000..7f9070a --- /dev/null +++ b/src/pyrite/utils/reg_if.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL */ +/* + +Copyright (c) 2026 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#ifndef REG_IF_H +#define REG_IF_H + +#include +#include + +struct reg_if { + const struct reg_if_ops *ops; + void *priv; + size_t size; + size_t offset; +}; + +struct reg_if_ops { + int (*read8)(const struct reg_if *reg, size_t offset, uint8_t *value); + int (*write8)(const struct reg_if *reg, size_t offset, uint8_t value); + int (*read16)(const struct reg_if *reg, size_t offset, uint16_t *value); + int (*write16)(const struct reg_if *reg, size_t offset, uint16_t value); + int (*read32)(const struct reg_if *reg, size_t offset, uint32_t *value); + int (*write32)(const struct reg_if *reg, size_t offset, uint32_t value); + int (*read64)(const struct reg_if *reg, size_t offset, uint64_t *value); + int (*write64)(const struct reg_if *reg, size_t offset, uint64_t value); + void (*close)(const struct reg_if *reg); +}; + +int reg_if_read8(const struct reg_if *reg, size_t offset, uint8_t *value); +int reg_if_write8(const struct reg_if *reg, size_t offset, uint8_t value); +int reg_if_read16(const struct reg_if *reg, size_t offset, uint16_t *value); +int reg_if_write16(const struct reg_if *reg, size_t offset, uint16_t value); +int reg_if_read32(const struct reg_if *reg, size_t offset, uint32_t *value); +int reg_if_write32(const struct reg_if *reg, size_t offset, uint32_t value); +int reg_if_read64(const struct reg_if *reg, size_t offset, uint64_t *value); +int reg_if_write64(const struct reg_if *reg, size_t offset, uint64_t value); +void reg_if_close(struct reg_if *reg); + +struct reg_if *reg_if_open_raw(void *regs, size_t size); + +struct reg_if *reg_if_open_offset(struct reg_if *reg, size_t offset, size_t size); + +#endif /* REG_IF_H */