Use algorithm to speed up fespi flash programming.

It's still really slow, at 854b/s, but it seems to work and it's a lot
of new code.

Change-Id: I9dd057bbcc81a56eb558b4f33ac35f6f03c23588
This commit is contained in:
Tim Newsome 2016-11-16 16:12:27 -08:00
parent 6686e71bd6
commit 18eedf996c
2 changed files with 595 additions and 364 deletions

View File

@ -41,6 +41,7 @@
#include "spi.h" #include "spi.h"
#include <jtag/jtag.h> #include <jtag/jtag.h>
#include <helper/time_support.h> #include <helper/time_support.h>
#include <target/algorithm.h>
/* Register offsets */ /* Register offsets */
@ -146,21 +147,21 @@
FESPI_READ_REG(FESPI_REG_FCTRL) | FESPI_FCTRL_EN) FESPI_READ_REG(FESPI_REG_FCTRL) | FESPI_FCTRL_EN)
struct fespi_flash_bank { struct fespi_flash_bank {
int probed; int probed;
uint32_t ctrl_base; uint32_t ctrl_base;
const struct flash_device *dev; const struct flash_device *dev;
}; };
struct fespi_target { struct fespi_target {
char *name; char *name;
uint32_t tap_idcode; uint32_t tap_idcode;
uint32_t ctrl_base; uint32_t ctrl_base;
}; };
//TODO !!! What is the right naming convention here? //TODO !!! What is the right naming convention here?
static const struct fespi_target target_devices[] = { static const struct fespi_target target_devices[] = {
/* name, tap_idcode, ctrl_base */ /* name, tap_idcode, ctrl_base */
{ "Freedom E300 SPI Flash", 0x10e31913 , 0x10014000 }, { "Freedom E300 SPI Flash", 0x10e31913 , 0x10014000 },
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };
@ -186,169 +187,169 @@ FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command)
} }
static int fespi_set_dir (struct flash_bank * bank, bool dir) { static int fespi_set_dir (struct flash_bank * bank, bool dir) {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
FESPI_WRITE_REG(FESPI_REG_FMT,
(FESPI_READ_REG(FESPI_REG_FMT) & ~(FESPI_FMT_DIR(0xFFFFFFFF))) |
FESPI_FMT_DIR(dir));
return ERROR_OK;
FESPI_WRITE_REG(FESPI_REG_FMT,
(FESPI_READ_REG(FESPI_REG_FMT) & ~(FESPI_FMT_DIR(0xFFFFFFFF))) |
FESPI_FMT_DIR(dir));
return ERROR_OK;
} }
static int fespi_txwm_wait(struct flash_bank *bank) { static int fespi_txwm_wait(struct flash_bank *bank) {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
while (!(FESPI_READ_REG(FESPI_REG_IP) & FESPI_IP_TXWM)); while (!(FESPI_READ_REG(FESPI_REG_IP) & FESPI_IP_TXWM));
return ERROR_OK;
return ERROR_OK;
} }
static int fespi_tx(struct flash_bank *bank, uint8_t in){ static int fespi_tx(struct flash_bank *bank, uint8_t in){
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
while ((int32_t) FESPI_READ_REG(FESPI_REG_TXFIFO) < 0); while ((int32_t) FESPI_READ_REG(FESPI_REG_TXFIFO) < 0);
FESPI_WRITE_REG(FESPI_REG_TXFIFO, in); FESPI_WRITE_REG(FESPI_REG_TXFIFO, in);
return ERROR_OK; return ERROR_OK;
} }
static uint8_t fespi_rx(struct flash_bank * bank) { static uint8_t fespi_rx(struct flash_bank * bank) {
struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base;
int32_t out; struct target *target = bank->target;
while ((out = (int32_t) FESPI_READ_REG(FESPI_REG_RXFIFO)) < 0); struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base;
return out & 0xFF; int32_t out;
while ((out = (int32_t) FESPI_READ_REG(FESPI_REG_RXFIFO)) < 0);
return out & 0xFF;
} }
//TODO!!! Why don't we need to call this after writing? //TODO!!! Why don't we need to call this after writing?
static int fespi_wip (struct flash_bank * bank, int timeout) { static int fespi_wip (struct flash_bank * bank, int timeout) {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
int64_t endtime; int64_t endtime;
fespi_set_dir(bank, FESPI_DIR_RX);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD); fespi_set_dir(bank, FESPI_DIR_RX);
endtime = timeval_ms() + timeout;
fespi_tx(bank, SPIFLASH_READ_STATUS); FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
fespi_rx(bank); endtime = timeval_ms() + timeout;
do { fespi_tx(bank, SPIFLASH_READ_STATUS);
fespi_rx(bank);
alive_sleep(1);
do {
fespi_tx(bank, 0);
if ((fespi_rx(bank) & SPIFLASH_BSY_BIT) == 0) { alive_sleep(1);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
fespi_set_dir(bank, FESPI_DIR_TX); fespi_tx(bank, 0);
return ERROR_OK; if ((fespi_rx(bank) & SPIFLASH_BSY_BIT) == 0) {
} FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
fespi_set_dir(bank, FESPI_DIR_TX);
} while (timeval_ms() < endtime); return ERROR_OK;
}
} while (timeval_ms() < endtime);
LOG_ERROR("timeout");
return ERROR_FAIL;
LOG_ERROR("timeout");
return ERROR_FAIL;
} }
static int fespi_erase_sector(struct flash_bank *bank, int sector) static int fespi_erase_sector(struct flash_bank *bank, int sector)
{ {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
int retval; int retval;
fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
fespi_txwm_wait(bank);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
fespi_tx(bank, fespi_info->dev->erase_cmd);
sector = bank->sectors[sector].offset;
fespi_tx(bank, sector >> 16);
fespi_tx(bank, sector >> 8);
fespi_tx(bank, sector);
fespi_txwm_wait(bank);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
retval = fespi_wip(bank, FESPI_MAX_TIMEOUT); fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
if (retval != ERROR_OK) fespi_txwm_wait(bank);
return retval;
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
return ERROR_OK; fespi_tx(bank, fespi_info->dev->erase_cmd);
sector = bank->sectors[sector].offset;
fespi_tx(bank, sector >> 16);
fespi_tx(bank, sector >> 8);
fespi_tx(bank, sector);
fespi_txwm_wait(bank);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
retval = fespi_wip(bank, FESPI_MAX_TIMEOUT);
if (retval != ERROR_OK)
return retval;
return ERROR_OK;
} }
static int fespi_erase(struct flash_bank *bank, int first, int last) static int fespi_erase(struct flash_bank *bank, int first, int last)
{ {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
int retval = ERROR_OK; int retval = ERROR_OK;
int sector; int sector;
LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
LOG_ERROR("Flash sector invalid");
return ERROR_FLASH_SECTOR_INVALID;
}
if (!(fespi_info->probed)) {
LOG_ERROR("Flash bank not probed");
return ERROR_FLASH_BANK_NOT_PROBED;
}
for (sector = first; sector <= last; sector++) {
if (bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
fespi_txwm_wait(bank);
/* Disable Hardware accesses*/
FESPI_DISABLE_HW_MODE();
/* poll WIP */ LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
if (retval != ERROR_OK)
return retval;
for (sector = first; sector <= last; sector++) { if (target->state != TARGET_HALTED) {
retval = fespi_erase_sector(bank, sector); LOG_ERROR("Target not halted");
if (retval != ERROR_OK) return ERROR_TARGET_NOT_HALTED;
break; }
keep_alive();
} if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
LOG_ERROR("Flash sector invalid");
/* Switch to HW mode before return to prompt */ return ERROR_FLASH_SECTOR_INVALID;
FESPI_ENABLE_HW_MODE(); }
return retval;
if (!(fespi_info->probed)) {
LOG_ERROR("Flash bank not probed");
return ERROR_FLASH_BANK_NOT_PROBED;
}
for (sector = first; sector <= last; sector++) {
if (bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
fespi_txwm_wait(bank);
/* Disable Hardware accesses*/
FESPI_DISABLE_HW_MODE();
/* poll WIP */
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
if (retval != ERROR_OK)
return retval;
for (sector = first; sector <= last; sector++) {
retval = fespi_erase_sector(bank, sector);
if (retval != ERROR_OK)
break;
keep_alive();
}
/* Switch to HW mode before return to prompt */
FESPI_ENABLE_HW_MODE();
return retval;
} }
static int fespi_protect(struct flash_bank *bank, int set, static int fespi_protect(struct flash_bank *bank, int set,
int first, int last) int first, int last)
{ {
int sector; int sector;
@ -357,27 +358,16 @@ static int fespi_protect(struct flash_bank *bank, int set,
return ERROR_OK; return ERROR_OK;
} }
/* This should write something less than or equal to a page.*/ static int slow_fespi_write_buffer(struct flash_bank *bank,
static int fespi_write_buffer(struct flash_bank *bank, const uint8_t *buffer, const uint8_t *buffer, uint32_t offset, uint32_t len)
uint32_t offset, uint32_t len)
{ {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
uint32_t ii; uint32_t ii;
//TODO!!! assert that len < page size //TODO!!! assert that len < page size
LOG_DEBUG("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32,
__func__, offset, len);
printf("%s: offset=0x%08" PRIx32 " len=0x%08" PRIx32,
__func__, offset, len);
fespi_tx(bank, SPIFLASH_WRITE_ENABLE); fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
fespi_txwm_wait(bank); fespi_txwm_wait(bank);
@ -390,240 +380,373 @@ static int fespi_write_buffer(struct flash_bank *bank, const uint8_t *buffer,
fespi_tx(bank, offset); fespi_tx(bank, offset);
for (ii = 0; ii < len; ii++) { for (ii = 0; ii < len; ii++) {
fespi_tx(bank, buffer[ii]); fespi_tx(bank, buffer[ii]);
} }
fespi_txwm_wait(bank); fespi_txwm_wait(bank);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO); FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
return ERROR_OK; return ERROR_OK;
} }
static int fespi_write(struct flash_bank *bank, const uint8_t *buffer, /* This should write something less than or equal to a page.*/
uint32_t offset, uint32_t count) static int fespi_write_buffer(struct flash_bank *bank, const uint8_t *buffer,
uint32_t chip_offset, uint32_t len,
struct working_area *algorithm_wa)
{ {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
uint32_t cur_count, page_size, page_offset;
int sector; LOG_DEBUG("%s: chip_offset=0x%08" PRIx32 " len=0x%08" PRIx32,
int retval = ERROR_OK; __func__, chip_offset, len);
LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32, if (!algorithm_wa) {
__func__, offset, count); return slow_fespi_write_buffer(bank, buffer, chip_offset, len);
}
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted"); struct working_area *data_wa;
return ERROR_TARGET_NOT_HALTED; unsigned data_wa_size = len + 4;
} while (1) {
if (data_wa_size < 128) {
if (offset + count > fespi_info->dev->size_in_bytes) { LOG_WARNING("Couldn't allocate data working area.");
LOG_WARNING("Write past end of flash. Extra data discarded."); return slow_fespi_write_buffer(bank, buffer, chip_offset, len);
count = fespi_info->dev->size_in_bytes - offset; }
} if (target_alloc_working_area_try(target, data_wa_size, &data_wa) ==
ERROR_OK) {
/* Check sector protection */ break;
for (sector = 0; sector < bank->num_sectors; sector++) { }
/* Start offset in or before this sector? */
/* End offset in or behind this sector? */ data_wa_size /= 2;
if ((offset < }
(bank->sectors[sector].offset + bank->sectors[sector].size))
&& ((offset + count - 1) >= bank->sectors[sector].offset) fespi_tx(bank, SPIFLASH_WRITE_ENABLE);
&& bank->sectors[sector].is_protected) { fespi_txwm_wait(bank);
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL; FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
}
} // Make the biggest possible block of txdata so we can write it in the
// minimum number of operations.
page_size = fespi_info->dev->pagesize; uint8_t *tx_data = malloc(4 + len);
if (!tx_data) {
fespi_txwm_wait(bank); LOG_ERROR("Couldn't allocate tx_data.");
return ERROR_FAIL;
/* Disable Hardware accesses*/ }
FESPI_DISABLE_HW_MODE();
tx_data[0] = SPIFLASH_PAGE_PROGRAM;
/* poll WIP */ tx_data[1] = chip_offset >> 16;
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT); tx_data[2] = chip_offset >> 8;
if (retval != ERROR_OK) tx_data[3] = chip_offset;
return retval; memcpy(tx_data + 4, buffer, len);
len += 4;
/* unaligned buffer head */
if (count > 0 && (offset & 3) != 0) { struct reg_param reg_params[3];
cur_count = 4 - (offset & 3); init_reg_param(&reg_params[0], "x10", 32, PARAM_OUT);
if (cur_count > count) init_reg_param(&reg_params[1], "x11", 32, PARAM_OUT);
cur_count = count; init_reg_param(&reg_params[2], "x12", 32, PARAM_OUT);
retval = fespi_write_buffer(bank, buffer, offset, buf_set_u32(reg_params[0].value, 0, 32, ctrl_base + FESPI_REG_TXFIFO);
cur_count); buf_set_u32(reg_params[1].value, 0, 32, data_wa->address);
if (retval != ERROR_OK) unsigned offset = 0;
goto err; while (len > 0) {
offset += cur_count; unsigned bytes = MIN(len, data_wa->size);
buffer += cur_count; buf_set_u32(reg_params[2].value, 0, 32, bytes);
count -= cur_count; int retval = target_write_buffer(target, data_wa->address, bytes,
} tx_data + offset);
if (retval != ERROR_OK) {
page_offset = offset % page_size; LOG_ERROR("Failed to write data to 0x%x: %d", data_wa->address,
/* central part, aligned words */ retval);
while (count >= 4) { goto error;
/* clip block at page boundary */ }
if (page_offset + count > page_size)
cur_count = page_size - page_offset; retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
else algorithm_wa->address,
cur_count = count & ~3; algorithm_wa->address + algorithm_wa->size - 4,
10000, NULL);
retval = fespi_write_buffer(bank, buffer, offset, if (retval != ERROR_OK) {
cur_count); LOG_ERROR("Failed to execute algorithm at 0x%x: %d", algorithm_wa->address,
if (retval != ERROR_OK) retval);
goto err; goto error;
}
page_offset = 0;
buffer += cur_count; len -= bytes;
offset += cur_count; offset += bytes;
count -= cur_count; }
keep_alive(); target_free_working_area(target, data_wa);
}
fespi_txwm_wait(bank);
/* buffer tail */ FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
if (count > 0)
retval = fespi_write_buffer(bank, buffer, offset, count); return ERROR_OK;
err: error:
/* Switch to HW mode before return to prompt */ target_free_working_area(target, data_wa);
FESPI_ENABLE_HW_MODE(); LOG_ERROR("Falling back to slow write.");
return retval; return slow_fespi_write_buffer(bank, buffer, chip_offset, len);
}
static int fespi_write(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
{
struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base;
uint32_t cur_count, page_size, page_offset;
int sector;
int retval = ERROR_OK;
LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
__func__, offset, count);
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (offset + count > fespi_info->dev->size_in_bytes) {
LOG_WARNING("Write past end of flash. Extra data discarded.");
count = fespi_info->dev->size_in_bytes - offset;
}
/* Check sector protection */
for (sector = 0; sector < bank->num_sectors; sector++) {
/* Start offset in or before this sector? */
/* End offset in or behind this sector? */
if ((offset <
(bank->sectors[sector].offset + bank->sectors[sector].size))
&& ((offset + count - 1) >= bank->sectors[sector].offset)
&& bank->sectors[sector].is_protected) {
LOG_ERROR("Flash sector %d protected", sector);
return ERROR_FAIL;
}
}
struct working_area *algorithm_wa;
static const uint32_t riscv_write_buffer_code[] = {
// a0 - address of FESPI_REG_TXFIFO
// a1 - start address of buffer
// a2 - size of buffer, in bytes
/* 0 */ 0x00052283, // lw t0,0(a0)
/* 4 */ 0xfe02cee3, // bltz t0,0
/* 8 */ 0x00058283, // lb t0,0(a1)
/* c */ 0x00552023, // sw t0,0(a0)
/* 10 */ 0x00158593, // addi a1,a1,1
/* 14 */ 0xfff60613, // addi a2,a2,-1
/* 18 */ 0xfec044e3, // bgtz a2,0
/* 1c */ 0x00100073, // ebreak
};
if (target_alloc_working_area(target, sizeof(riscv_write_buffer_code),
&algorithm_wa) != ERROR_OK) {
LOG_WARNING("Couldn't allocate %ld-byte working area.",
sizeof(riscv_write_buffer_code));
algorithm_wa = NULL;
} else {
uint8_t code[sizeof(riscv_write_buffer_code)];
target_buffer_set_u32_array(target, code,
ARRAY_SIZE(riscv_write_buffer_code), riscv_write_buffer_code);
retval = target_write_buffer(target, algorithm_wa->address,
sizeof(code), code);
if (retval != ERROR_OK) {
LOG_ERROR("Failed to write code to 0x%x: %d", algorithm_wa->address,
retval);
target_free_working_area(target, algorithm_wa);
algorithm_wa = NULL;
}
}
page_size = fespi_info->dev->pagesize;
fespi_txwm_wait(bank);
/* Disable Hardware accesses*/
FESPI_DISABLE_HW_MODE();
/* poll WIP */
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
if (retval != ERROR_OK)
return retval;
/* unaligned buffer head */
if (count > 0 && (offset & 3) != 0) {
cur_count = 4 - (offset & 3);
if (cur_count > count)
cur_count = count;
retval = fespi_write_buffer(bank, buffer, offset,
cur_count, algorithm_wa);
if (retval != ERROR_OK)
goto err;
offset += cur_count;
buffer += cur_count;
count -= cur_count;
}
page_offset = offset % page_size;
/* central part, aligned words */
while (count >= 4) {
/* clip block at page boundary */
if (page_offset + count > page_size)
cur_count = page_size - page_offset;
else
cur_count = count & ~3;
retval = fespi_write_buffer(bank, buffer, offset,
cur_count, algorithm_wa);
if (retval != ERROR_OK)
goto err;
page_offset = 0;
buffer += cur_count;
offset += cur_count;
count -= cur_count;
keep_alive();
}
/* buffer tail */
if (count > 0)
retval = fespi_write_buffer(bank, buffer, offset, count, algorithm_wa);
err:
if (algorithm_wa) {
target_free_working_area(target, algorithm_wa);
}
/* Switch to HW mode before return to prompt */
FESPI_ENABLE_HW_MODE();
return retval;
} }
/* Return ID of flash device */ /* Return ID of flash device */
/* On exit, SW mode is kept */ /* On exit, SW mode is kept */
static int fespi_read_flash_id(struct flash_bank *bank, uint32_t *id) static int fespi_read_flash_id(struct flash_bank *bank, uint32_t *id)
{ {
struct target *target = bank->target; struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base = fespi_info->ctrl_base; uint32_t ctrl_base = fespi_info->ctrl_base;
int retval; int retval;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
fespi_txwm_wait(bank);
/* Disable Hardware accesses*/
FESPI_DISABLE_HW_MODE();
/* poll WIP */ if (target->state != TARGET_HALTED) {
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT); LOG_ERROR("Target not halted");
if (retval != ERROR_OK) return ERROR_TARGET_NOT_HALTED;
return retval; }
fespi_set_dir(bank, FESPI_DIR_RX); fespi_txwm_wait(bank);
/* Send SPI command "read ID" */
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
fespi_tx(bank, SPIFLASH_READ_ID);
/* Send dummy bytes to actually read the ID.*/
fespi_tx(bank, 0);
fespi_tx(bank, 0);
fespi_tx(bank, 0);
/* read ID from Receive Register */
*id = 0;
fespi_rx(bank);
*id = fespi_rx(bank);
*id |= (fespi_rx(bank) << 8);
*id |= (fespi_rx(bank) << 16);
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO); /* Disable Hardware accesses*/
FESPI_DISABLE_HW_MODE();
fespi_set_dir(bank, FESPI_DIR_TX); /* poll WIP */
retval = fespi_wip(bank, FESPI_PROBE_TIMEOUT);
return ERROR_OK; if (retval != ERROR_OK)
} return retval;
static int fespi_probe(struct flash_bank *bank) fespi_set_dir(bank, FESPI_DIR_RX);
{
struct target *target = bank->target;
struct fespi_flash_bank *fespi_info = bank->driver_priv;
uint32_t ctrl_base;
struct flash_sector *sectors;
uint32_t id = 0; /* silence uninitialized warning */
const struct fespi_target *target_device;
int retval;
if (fespi_info->probed)
free(bank->sectors);
fespi_info->probed = 0;
for (target_device = target_devices ; target_device->name ; ++target_device)
if (target_device->tap_idcode == target->tap->idcode)
break;
if (!target_device->name) {
LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable",
target->tap->idcode);
return ERROR_FAIL;
}
ctrl_base = target_device->ctrl_base;
fespi_info->ctrl_base = ctrl_base;
LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32,
target_device->name, bank->base);
/* read and decode flash ID; returns in SW mode */
// TODO!!! Pass these arguments in to the driver
// Elsewhere this driver assumes these are set this way,
// but should really save and restore at the entry points.
FESPI_WRITE_REG(FESPI_REG_SCKDIV, 3);
FESPI_WRITE_REG(FESPI_REG_TXCTRL, FESPI_TXWM(1));
fespi_set_dir(bank, FESPI_DIR_TX);
retval = fespi_read_flash_id(bank, &id); /* Send SPI command "read ID" */
FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_HOLD);
FESPI_ENABLE_HW_MODE(); fespi_tx(bank, SPIFLASH_READ_ID);
if (retval != ERROR_OK) /* Send dummy bytes to actually read the ID.*/
return retval; fespi_tx(bank, 0);
fespi_tx(bank, 0);
fespi_info->dev = NULL; fespi_tx(bank, 0);
for (const struct flash_device *p = flash_devices; p->name ; p++)
if (p->device_id == id) { /* read ID from Receive Register */
fespi_info->dev = p; *id = 0;
break; fespi_rx(bank);
} *id = fespi_rx(bank);
*id |= (fespi_rx(bank) << 8);
if (!fespi_info->dev) { *id |= (fespi_rx(bank) << 16);
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
return ERROR_FAIL; FESPI_WRITE_REG(FESPI_REG_CSMODE, FESPI_CSMODE_AUTO);
}
fespi_set_dir(bank, FESPI_DIR_TX);
LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
fespi_info->dev->name, fespi_info->dev->device_id); return ERROR_OK;
}
/* Set correct size value */
bank->size = fespi_info->dev->size_in_bytes; static int fespi_probe(struct flash_bank *bank)
{
/* create and fill sectors array */ struct target *target = bank->target;
bank->num_sectors = struct fespi_flash_bank *fespi_info = bank->driver_priv;
fespi_info->dev->size_in_bytes / fespi_info->dev->sectorsize; uint32_t ctrl_base;
sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); struct flash_sector *sectors;
if (sectors == NULL) { uint32_t id = 0; /* silence uninitialized warning */
LOG_ERROR("not enough memory"); const struct fespi_target *target_device;
return ERROR_FAIL; int retval;
}
if (fespi_info->probed)
for (int sector = 0; sector < bank->num_sectors; sector++) { free(bank->sectors);
sectors[sector].offset = sector * fespi_info->dev->sectorsize; fespi_info->probed = 0;
sectors[sector].size = fespi_info->dev->sectorsize;
sectors[sector].is_erased = -1; for (target_device = target_devices ; target_device->name ; ++target_device)
sectors[sector].is_protected = 1; if (target_device->tap_idcode == target->tap->idcode)
} break;
if (!target_device->name) {
bank->sectors = sectors; LOG_ERROR("Device ID 0x%" PRIx32 " is not known as FESPI capable",
fespi_info->probed = 1; target->tap->idcode);
return ERROR_OK; return ERROR_FAIL;
} }
ctrl_base = target_device->ctrl_base;
fespi_info->ctrl_base = ctrl_base;
LOG_DEBUG("Valid FESPI on device %s at address 0x%" PRIx32,
target_device->name, bank->base);
/* read and decode flash ID; returns in SW mode */
// TODO!!! Pass these arguments in to the driver
// Elsewhere this driver assumes these are set this way,
// but should really save and restore at the entry points.
FESPI_WRITE_REG(FESPI_REG_SCKDIV, 3);
FESPI_WRITE_REG(FESPI_REG_TXCTRL, FESPI_TXWM(1));
fespi_set_dir(bank, FESPI_DIR_TX);
retval = fespi_read_flash_id(bank, &id);
FESPI_ENABLE_HW_MODE();
if (retval != ERROR_OK)
return retval;
fespi_info->dev = NULL;
for (const struct flash_device *p = flash_devices; p->name ; p++)
if (p->device_id == id) {
fespi_info->dev = p;
break;
}
if (!fespi_info->dev) {
LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
return ERROR_FAIL;
}
LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
fespi_info->dev->name, fespi_info->dev->device_id);
/* Set correct size value */
bank->size = fespi_info->dev->size_in_bytes;
/* create and fill sectors array */
bank->num_sectors =
fespi_info->dev->size_in_bytes / fespi_info->dev->sectorsize;
sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
if (sectors == NULL) {
LOG_ERROR("not enough memory");
return ERROR_FAIL;
}
for (int sector = 0; sector < bank->num_sectors; sector++) {
sectors[sector].offset = sector * fespi_info->dev->sectorsize;
sectors[sector].size = fespi_info->dev->sectorsize;
sectors[sector].is_erased = -1;
sectors[sector].is_protected = 1;
}
bank->sectors = sectors;
fespi_info->probed = 1;
return ERROR_OK;
}
static int fespi_auto_probe(struct flash_bank *bank) static int fespi_auto_probe(struct flash_bank *bank)
{ {
@ -639,19 +762,19 @@ static int fespi_protect_check(struct flash_bank *bank)
return ERROR_OK; return ERROR_OK;
} }
static int get_fespi_info(struct flash_bank *bank, char *buf, int buf_size) static int get_fespi_info(struct flash_bank *bank, char *buf, int buf_size)
{ {
struct fespi_flash_bank *fespi_info = bank->driver_priv; struct fespi_flash_bank *fespi_info = bank->driver_priv;
if (!(fespi_info->probed)) { if (!(fespi_info->probed)) {
snprintf(buf, buf_size, snprintf(buf, buf_size,
"\nFESPI flash bank not probed yet\n"); "\nFESPI flash bank not probed yet\n");
return ERROR_OK; return ERROR_OK;
} }
snprintf(buf, buf_size, "\nFESPI flash information:\n" snprintf(buf, buf_size, "\nFESPI flash information:\n"
" Device \'%s\' (ID 0x%08" PRIx32 ")\n", " Device \'%s\' (ID 0x%08" PRIx32 ")\n",
fespi_info->dev->name, fespi_info->dev->device_id); fespi_info->dev->name, fespi_info->dev->device_id);
return ERROR_OK; return ERROR_OK;
} }

View File

@ -7,12 +7,14 @@
#endif #endif
#include "target.h" #include "target.h"
#include "target/algorithm.h"
#include "target_type.h" #include "target_type.h"
#include "log.h" #include "log.h"
#include "jtag/jtag.h" #include "jtag/jtag.h"
#include "opcodes.h" #include "opcodes.h"
#include "register.h" #include "register.h"
#include "breakpoints.h" #include "breakpoints.h"
#include "helper/time_support.h"
/** /**
* Since almost everything can be accomplish by scanning the dbus register, all * Since almost everything can be accomplish by scanning the dbus register, all
@ -1291,6 +1293,7 @@ static int register_get(struct reg *reg)
return ERROR_OK; return ERROR_OK;
} else if (reg->number == REG_PC) { } else if (reg->number == REG_PC) {
buf_set_u32(reg->value, 0, 32, info->dpc); buf_set_u32(reg->value, 0, 32, info->dpc);
reg->valid = true;
LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc); LOG_DEBUG("%s=0x%" PRIx64 " (cached)", reg->name, info->dpc);
return ERROR_OK; return ERROR_OK;
} else if (reg->number >= REG_FPR0 && reg->number <= REG_FPR31) { } else if (reg->number >= REG_FPR0 && reg->number <= REG_FPR31) {
@ -2628,11 +2631,114 @@ static int riscv_get_gdb_reg_list(struct target *target,
return ERROR_OK; return ERROR_OK;
} }
int riscv_arch_state(struct target *target) static int riscv_arch_state(struct target *target)
{ {
return ERROR_OK; return ERROR_OK;
} }
// Algorithm must end with a software breakpoint instruction.
static int riscv_run_algorithm(struct target *target, int num_mem_params,
struct mem_param *mem_params, int num_reg_params,
struct reg_param *reg_params, uint32_t entry_point,
uint32_t exit_point, int timeout_ms, void *arch_info)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
if (num_mem_params > 0) {
LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
return ERROR_FAIL;
}
if (target->state != TARGET_HALTED) {
LOG_WARNING("target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/// Save registers
if (register_get(&target->reg_cache->reg_list[REG_PC]) != ERROR_OK) {
return ERROR_FAIL;
}
uint64_t saved_pc = reg_cache_get(target, REG_PC);
uint64_t saved_regs[32];
for (int i = 0; i < num_reg_params; i++) {
LOG_DEBUG("save %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
if (!r) {
LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
return ERROR_FAIL;
}
if (r->size != reg_params[i].size) {
LOG_ERROR("Register %s is %d bits instead of %d bits.",
reg_params[i].reg_name, r->size, reg_params[i].size);
return ERROR_FAIL;
}
if (r->number > REG_XPR31) {
LOG_ERROR("Only GPRs can be use as argument registers.");
return ERROR_FAIL;
}
if (register_get(r) != ERROR_OK) {
return ERROR_FAIL;
}
saved_regs[r->number] = buf_get_u64(r->value, 0, info->xlen);
if (register_set(r, reg_params[i].value) != ERROR_OK) {
return ERROR_FAIL;
}
}
/// Run algorithm
LOG_DEBUG("resume at 0x%x", entry_point);
if (riscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK) {
return ERROR_FAIL;
}
int64_t start = timeval_ms();
while (target->state != TARGET_HALTED) {
LOG_DEBUG("poll()");
int64_t now = timeval_ms();
if (now - start > timeout_ms) {
LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms);
return ERROR_TARGET_TIMEOUT;
}
int result = riscv_poll(target);
if (result != ERROR_OK) {
return result;
}
}
if (register_get(&target->reg_cache->reg_list[REG_PC]) != ERROR_OK) {
return ERROR_FAIL;
}
uint64_t final_pc = reg_cache_get(target, REG_PC);
if (final_pc != exit_point) {
LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%" PRIx32,
final_pc, exit_point);
return ERROR_FAIL;
}
/// Restore registers
uint8_t buf[8];
buf_set_u64(buf, 0, info->xlen, saved_pc);
if (register_set(&target->reg_cache->reg_list[REG_PC], buf) != ERROR_OK) {
return ERROR_FAIL;
}
for (int i = 0; i < num_reg_params; i++) {
LOG_DEBUG("restore %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]);
if (register_set(r, buf) != ERROR_OK) {
return ERROR_FAIL;
}
}
return ERROR_OK;
}
struct target_type riscv_target = struct target_type riscv_target =
{ {
.name = "riscv", .name = "riscv",
@ -2663,4 +2769,6 @@ struct target_type riscv_target =
.remove_watchpoint = riscv_remove_watchpoint, .remove_watchpoint = riscv_remove_watchpoint,
.arch_state = riscv_arch_state, .arch_state = riscv_arch_state,
.run_algorithm = riscv_run_algorithm,
}; };