pyrite: Initial commit of pyrite flashing utility

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2026-02-19 21:46:09 -08:00
parent 8f40d3a426
commit 589a80f582
15 changed files with 4269 additions and 0 deletions

1
src/pyrite/lib/taxi Symbolic link
View File

@@ -0,0 +1 @@
../../../

40
src/pyrite/utils/Makefile Normal file
View File

@@ -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

149
src/pyrite/utils/bitfile.c Normal file
View File

@@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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);
}
}

View File

@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL */
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#ifndef BITFILE_H
#define BITFILE_H
#include <stddef.h>
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

112
src/pyrite/utils/flash.c Normal file
View File

@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "flash.h"
#include <stdlib.h>
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);
}

77
src/pyrite/utils/flash.h Normal file
View File

@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL */
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#ifndef FLASH_H
#define FLASH_H
#include <stdint.h>
#include <unistd.h>
#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 */

View File

@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "flash.h"
#include <stdio.h>
#include <stdlib.h>
#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
};

View File

@@ -0,0 +1,670 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "flash.h"
#include <stdio.h>
#include <stdlib.h>
#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, &reg_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
};

323
src/pyrite/utils/fpga_id.c Normal file
View File

@@ -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;
}

312
src/pyrite/utils/fpga_id.h Normal file
View File

@@ -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 */

1491
src/pyrite/utils/pyrite.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include "reg_block.h"
#include <stdlib.h>
#include <stdio.h>
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);
}

View File

@@ -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 <stdint.h>
#include <unistd.h>
#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 */

259
src/pyrite/utils/reg_if.c Normal file
View File

@@ -0,0 +1,259 @@
// SPDX-License-Identifier: GPL
/*
Copyright (c) 2026 FPGA Ninja, LLC
Authors:
- Alex Forencich
*/
#include <stdlib.h>
#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 = &reg_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 = &reg_if_offset_ops;
reg->priv = regs;
reg->size = size;
reg->offset = offset;
return reg;
}

50
src/pyrite/utils/reg_if.h Normal file
View File

@@ -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 <stdint.h>
#include <stddef.h>
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 */