355 lines
11 KiB
C
355 lines
11 KiB
C
//
|
|
// Copyright (C) 2013-2014 Michael Geszkiewicz
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include "types.h"
|
|
#include "bitfile.h"
|
|
#include "eeprom.h"
|
|
#include "eeprom_local.h"
|
|
#include "eeprom_remote.h"
|
|
#include "boards.h"
|
|
|
|
u8 boot_block[BOOT_BLOCK_SIZE] = {
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xAA, 0x99, 0x55, 0x66, 0x31, 0xE1,
|
|
0xFF, 0xFF, 0x32, 0x61, 0x00, 0x00, 0x32, 0x81,
|
|
0x0B, 0x08, 0x32, 0xA1, 0x00, 0x00, 0x32, 0xC1,
|
|
0x0B, 0x01, 0x30, 0xA1, 0x00, 0x00, 0x33, 0x01,
|
|
0x21, 0x00, 0x32, 0x01, 0x00, 0x1F, 0x30, 0xA1,
|
|
0x00, 0x0E, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00,
|
|
0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00
|
|
};
|
|
|
|
u8 page_buffer[PAGE_SIZE];
|
|
u8 file_buffer[SECTOR_SIZE];
|
|
|
|
spi_eeprom_dev_t eeprom_access;
|
|
|
|
char *eeprom_get_flash_type(u8 flash_id) {
|
|
switch (flash_id) {
|
|
case ID_EEPROM_1M: return "1Mb";
|
|
case ID_EEPROM_2M: return "2Mb";
|
|
case ID_EEPROM_4M: return "4Mb";
|
|
case ID_EEPROM_8M: return "8Mb";
|
|
case ID_EEPROM_16M: return "16Mb";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
static u32 eeprom_get_flash_size(u8 flash_id) {
|
|
switch (flash_id) {
|
|
case ID_EEPROM_1M: return 0x100000 / 8;
|
|
case ID_EEPROM_2M: return 0x200000 / 8;
|
|
case ID_EEPROM_4M: return 0x400000 / 8;
|
|
case ID_EEPROM_8M: return 0x800000 / 8;
|
|
case ID_EEPROM_16M: return 0x1000000 / 8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// modify MSB of boot block jmp address to user area
|
|
void eeprom_prepare_boot_block(u8 flash_id) {
|
|
switch (flash_id) {
|
|
case ID_EEPROM_1M: boot_block[25] = 0x01; break;
|
|
case ID_EEPROM_2M: boot_block[25] = 0x02; break;
|
|
case ID_EEPROM_4M: boot_block[25] = 0x04; break;
|
|
case ID_EEPROM_8M: boot_block[25] = 0x08; break;
|
|
case ID_EEPROM_16M: boot_block[25] = 0x10; break;
|
|
}
|
|
}
|
|
|
|
u32 eeprom_calc_user_space(u8 flash_id) {
|
|
switch (flash_id) {
|
|
case ID_EEPROM_1M: return 0x10000; break;
|
|
case ID_EEPROM_2M: return 0x20000; break;
|
|
case ID_EEPROM_4M: return 0x40000; break;
|
|
case ID_EEPROM_8M: return 0x80000; break;
|
|
case ID_EEPROM_16M: return 0x100000; break;
|
|
default: return 0x80000; break;
|
|
}
|
|
}
|
|
|
|
static int check_boot(llio_t *self) {
|
|
int i;
|
|
|
|
eeprom_access.read_page(self, 0x0, &page_buffer);
|
|
for (i = 0; i < BOOT_BLOCK_SIZE; i++) {
|
|
if (boot_block[i] != page_buffer[i]) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void write_boot(llio_t *self) {
|
|
printf("Erasing sector 0 for boot block\n");
|
|
eeprom_access.erase_sector(self, BOOT_ADDRESS);
|
|
memset(&file_buffer, 0, PAGE_SIZE);
|
|
memcpy(&file_buffer, &boot_block, BOOT_BLOCK_SIZE);
|
|
eeprom_access.write_page(self, 0x0, &file_buffer);
|
|
printf("BootBlock installed\n");
|
|
}
|
|
|
|
int start_programming(llio_t *self, u32 start_address, int fsize) {
|
|
board_t *board = self->board;
|
|
u32 sec_addr;
|
|
int esectors, sector, max_sectors;
|
|
struct timeval tv1, tv2;
|
|
|
|
esectors = (fsize - 1) / SECTOR_SIZE;
|
|
if (board->fallback_support == 1) {
|
|
if (start_address == FALLBACK_ADDRESS) {
|
|
max_sectors = eeprom_calc_user_space(board->flash_id) / SECTOR_SIZE - 1;
|
|
} else {
|
|
max_sectors = eeprom_calc_user_space(board->flash_id) / SECTOR_SIZE;
|
|
}
|
|
} else {
|
|
max_sectors = eeprom_get_flash_size(board->flash_id) / SECTOR_SIZE;
|
|
}
|
|
if (esectors > max_sectors) {
|
|
printf("File Size too large to fit\n");
|
|
return -1;
|
|
}
|
|
printf("EEPROM sectors to write: %d, max sectors in area: %d\n", esectors + 1, max_sectors);
|
|
sec_addr = start_address;
|
|
printf("Erasing EEPROM sectors starting from 0x%X...\n", (unsigned int) start_address);
|
|
printf(" |");
|
|
fflush(stdout);
|
|
gettimeofday(&tv1, NULL);
|
|
for (sector = 0; sector <= esectors; sector++) {
|
|
eeprom_access.erase_sector(self, sec_addr);
|
|
sec_addr = sec_addr + SECTOR_SIZE;
|
|
printf("E");
|
|
fflush(stdout);
|
|
}
|
|
if (board->llio.verbose == 1) {
|
|
gettimeofday(&tv2, NULL);
|
|
printf("\n Erasing time: %.2f seconds", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
|
|
(double) (tv2.tv_sec - tv1.tv_sec));
|
|
}
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
int eeprom_write(llio_t *self, char *bitfile_name, u32 start_address, int fix_boot_flag) {
|
|
board_t *board = self->board;
|
|
int bytesread, i;
|
|
u32 eeprom_addr;
|
|
char part_name[32];
|
|
char board_name[32];
|
|
struct stat file_stat;
|
|
FILE *fp;
|
|
struct timeval tv1, tv2;
|
|
|
|
if (stat(bitfile_name, &file_stat) != 0) {
|
|
printf("Can't find file %s\n", bitfile_name);
|
|
return -1;
|
|
}
|
|
|
|
fp = fopen(bitfile_name, "rb");
|
|
if (fp == NULL) {
|
|
printf("Can't open file %s: %s\n", bitfile_name, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (print_bitfile_header(fp, (char*) &part_name, (char*) &board_name, board->llio.verbose) == -1) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
if (board->recover == 0) {
|
|
if (strchr(board->llio.fpga_part_number, '|') == NULL) {
|
|
if (strcmp(part_name, board->llio.fpga_part_number) != 0) {
|
|
printf("Error: wrong bitfile destination device: %s, should be %s\n", part_name, board->llio.fpga_part_number);
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
}
|
|
if (!check_board_name(self->board_name, board_name, bitfile_name)) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
}
|
|
// if board doesn't support fallback there is no boot block
|
|
if (board->fallback_support == 1) {
|
|
if (check_boot(self) == -1) {
|
|
if (fix_boot_flag) {
|
|
write_boot(self);
|
|
if (check_boot(self) == -1) {
|
|
printf("Failed to write valid boot sector\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
} else {
|
|
printf("Error: BootSector is invalid\n");
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
} else {
|
|
printf("Boot sector OK\n");
|
|
}
|
|
}
|
|
|
|
if (eeprom_access.start_programming(self, start_address, file_stat.st_size) == -1) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
printf("Programming EEPROM sectors starting from 0x%X...\n", (unsigned int) start_address);
|
|
printf(" |");
|
|
fflush(stdout);
|
|
gettimeofday(&tv1, NULL);
|
|
eeprom_addr = start_address;
|
|
while (!feof(fp)) {
|
|
bytesread = fread(&file_buffer, 1, 8192, fp);
|
|
i = 0;
|
|
while (i < bytesread) {
|
|
eeprom_access.write_page(self, eeprom_addr, &file_buffer[i]);
|
|
i += PAGE_SIZE;
|
|
eeprom_addr += PAGE_SIZE;
|
|
}
|
|
printf("W");
|
|
fflush(stdout);
|
|
}
|
|
|
|
fclose(fp);
|
|
printf("\n");
|
|
if (board->llio.verbose == 1) {
|
|
gettimeofday(&tv2, NULL);
|
|
printf(" Programming time: %.2f seconds\n", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
|
|
(double) (tv2.tv_sec - tv1.tv_sec));
|
|
}
|
|
printf("Board configuration updated successfully.\n");
|
|
return 0;
|
|
}
|
|
|
|
int eeprom_verify(llio_t *self, char *bitfile_name, u32 start_address) {
|
|
board_t *board = self->board;
|
|
int bytesread, i, bindex;
|
|
u32 eeprom_addr;
|
|
char part_name[32];
|
|
char board_name[32];
|
|
struct stat file_stat;
|
|
FILE *fp;
|
|
struct timeval tv1, tv2;
|
|
|
|
if (stat(bitfile_name, &file_stat) != 0) {
|
|
printf("Can't find file %s\n", bitfile_name);
|
|
return -1;
|
|
}
|
|
|
|
fp = fopen(bitfile_name, "rb");
|
|
if (fp == NULL) {
|
|
printf("Can't open file %s: %s\n", bitfile_name, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (print_bitfile_header(fp, (char*) &part_name, (char*) &board_name, board->llio.verbose) == -1) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
if (strchr(board->llio.fpga_part_number, '|') == NULL) {
|
|
if (strcmp(part_name, board->llio.fpga_part_number) != 0) {
|
|
printf("Error: wrong bitfile destination device: %s, should be %s\n", part_name, board->llio.fpga_part_number);
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
}
|
|
if (!check_board_name(self->board_name, board_name, bitfile_name)) {
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
// if board doesn't support fallback there is no boot block
|
|
if (board->fallback_support == 1) {
|
|
if (check_boot(self) == -1) {
|
|
printf("Error: BootSector is invalid\n");
|
|
fclose(fp);
|
|
return -1;
|
|
} else {
|
|
printf("Boot sector OK\n");
|
|
}
|
|
}
|
|
|
|
printf("Verifying EEPROM sectors starting from 0x%X...\n", (unsigned int) start_address);
|
|
printf(" |");
|
|
fflush(stdout);
|
|
gettimeofday(&tv1, NULL);
|
|
eeprom_addr = start_address;
|
|
while (!feof(fp)) {
|
|
bytesread = fread(&file_buffer, 1, 8192, fp);
|
|
bindex = 0;
|
|
while (bindex < bytesread) {
|
|
eeprom_access.read_page(self, eeprom_addr, &page_buffer);
|
|
for (i = 0; i < PAGE_SIZE; i++, bindex++) {
|
|
if (file_buffer[bindex] != page_buffer[i]) {
|
|
printf("\nError at 0x%X expected: 0x%X but read: 0x%X\n", eeprom_addr + i, file_buffer[bindex], page_buffer[i]);
|
|
return -1;
|
|
}
|
|
}
|
|
eeprom_addr += PAGE_SIZE;
|
|
}
|
|
printf("V");
|
|
fflush(stdout);
|
|
}
|
|
|
|
fclose(fp);
|
|
printf("\n");
|
|
if (board->llio.verbose == 1) {
|
|
gettimeofday(&tv2, NULL);
|
|
printf(" Verification time: %.2f seconds\n", (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
|
|
(double) (tv2.tv_sec - tv1.tv_sec));
|
|
}
|
|
printf("Board configuration verified successfully.\n");
|
|
return 0;
|
|
}
|
|
|
|
void eeprom_init(llio_t *self) {
|
|
board_t *board = self->board;
|
|
|
|
switch (board->flash) {
|
|
case BOARD_FLASH_NONE:
|
|
break;
|
|
case BOARD_FLASH_HM2:
|
|
case BOARD_FLASH_IO:
|
|
case BOARD_FLASH_GPIO:
|
|
case BOARD_FLASH_EPP:
|
|
open_spi_access_local(self);
|
|
break;
|
|
case BOARD_FLASH_REMOTE:
|
|
open_spi_access_remote(self);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void eeprom_cleanup(llio_t *self) {
|
|
board_t *board = self->board;
|
|
|
|
switch (board->flash) {
|
|
case BOARD_FLASH_NONE:
|
|
break;
|
|
case BOARD_FLASH_HM2:
|
|
case BOARD_FLASH_IO:
|
|
case BOARD_FLASH_GPIO:
|
|
case BOARD_FLASH_EPP:
|
|
close_spi_access_local(self);
|
|
break;
|
|
case BOARD_FLASH_REMOTE:
|
|
close_spi_access_remote(self);
|
|
break;
|
|
}
|
|
}
|