Commit ea6f64d3 authored by Qiang Du's avatar Qiang Du

Fixed synthesize process using customized makefile with hdl-make.

Finished building chains to implement bit file generation using data2mem. Refer to http://www.ohwr.org/issues/1006

Added tools/eb-*.c for Etherbone support of wrc console, info & flash programming.

Added impact script for JTAG bit file programming.

Updated README.md
parent 8a1088a3
include dir_list.mk
SYN_TOP = $(TOP)/syn/cute_wr/wr_core_demo
EB_TGT = $(EB_DIR)/api/libetherbone.a
BIT_TGT = $(SYN_TOP)/cute_top_wrc.bit
WRC_TGT = $(FW_DIR)/wrc.elf
.PHONY: all install clean distclean etherbone etherbone-clean etherbone-install tools tools-clean tools-install toolchain firmware gateware firmware-clean gateware-clean program program-clean
all: etherbone tools firmware gateware
install: etherbone-install tools-install
clean: etherbone-clean tools-clean toolchain-clean firmware-clean gateware-clean program-clean
distclean: clean
git clean -xfd .
for i in gateware firmware; do cd submodules/$$i; git clean -xfd .; cd $(TOP); done
etherbone: $(EB_TGT)
firmware: $(WRC_TGT)
gateware: $(BIT_TGT)
$(EB_TGT):
$(MAKE) -C $(EB_DIR)/api all
etherbone-clean:
$(MAKE) -C $(EB_DIR)/api clean
etherbone-install:
$(MAKE) -C $(EB_DIR)/api install
tools: etherbone
$(MAKE) -C tools EB=$(EB_DIR)/api all
tools-clean:
$(MAKE) -C tools EB=$(EB_DIR)/api clean
tools-install:
$(MAKE) -C tools EB=$(EB_DIR)/api install
$(FW_DIR)/.config:
$(MAKE) -C $(FW_DIR) etherbone_defconfig
$(WRC_TGT): $(EB_TGT) $(FW_DIR)/.config
$(MAKE) -C $(FW_DIR) EB=$(EB_DIR)/api all
firmware-clean:
$(MAKE) -C $(FW_DIR) EB=$(EB_DIR)/api clean
$(BIT_TGT): firmware
$(MAKE) -C $(SYN_TOP) all
gateware-clean:
$(MAKE) -C $(SYN_TOP) clean
program: $(BIT_TGT)
./tools/impact_cutewr $<
program-clean:
rm -f *.bit
rm -f _impactbatch.log
# not for 64 bit arch
gcc-4.5.3-lm32.tar.xz:
wget http://www.ohwr.org/attachments/1301/gcc-4.5.3-lm32.tar.xz
toolchain: gcc-4.5.3-lm32.tar.xz
tar xvJf gcc-4.5.3-lm32.tar.xz
mv lm32 toolchain
touch toolchain
toolchain-clean:
rm -rf toolchain
# CUTE-wr projects
## Code structure
* Gateware:
`submodules/wr-cores`
cute-wr branch that work on hardware version 2.1
* Firmware:
`submodules/wrpc-sw`
Full support of substituting wrpc RAM in target bit file using data2mem. See http://www.ohwr.org/issues/1006
* Software:
multiboot support
TBD
## Building
git submodule update --recursive --init
make
TOP := $(dir $(lastword $(MAKEFILE_LIST)))
SUBMODULES := $(TOP)submodules
GW_DIR := $(SUBMODULES)/wr-cores
FW_DIR := $(SUBMODULES)/wrpc-sw
EB_DIR := $(GW_DIR)/ip_cores/etherbone-core
hdl-make @ b0ccccc6
Subproject commit aabe6c840ed4d8414dd62bb67781e10672184103
Subproject commit b0ccccc6a3c687408cf706ca1ec93556872598b6
TARGET = cute_top
PROJECT = cute_top_wrc
include ../../../dir_list.mk
.PHONY: all
all: $(TARGET)_wrc.bit
$(PROJECT).xise: Manifest.py
python2.7 $(TOP)submodules/hdl-make/hdlmake ise-project
# Xilinx UG658
$(TARGET)_wrc.bit: $(FW_DIR)/wrc.elf $(TARGET).bit $(TARGET)_bd.bmm
data2mem -bm $(word 3, $^) -bd $< -bt $(word 2, $^) -o b $@
$(TARGET).bmm: $(GW_DIR)/top/cute_wr/wr_core_demo/cute.bmm
cp $< $@
$(TARGET)_bd.bmm $(TARGET).bit: $(PROJECT).xise $(TARGET).bmm
printf "project open $< \n\
project set {Hierarchy Separator} / \n\
xfile add $(word 2, $^) \n\
process run {Generate Programming File} -force rerun" | xtclsh
ISE_CRAP := *.b *.html $(TARGET).bgn $(TARGET).bld $(TARGET).cmd_log *.drc $(TARGET).lso *.ncd $(TARGET).ngc $(TARGET).ngd $(TARGET).ngr $(TARGET).pad $(TARGET).par $(TARGET).pcf $(TARGET).prj $(TARGET).ptwx $(TARGET).stx $(TARGET).syr $(TARGET).twr $(TARGET).twx $(TARGET).gise $(TARGET).unroutes $(TARGET).ut $(TARGET).xpi $(TARGET).xst $(TARGET)_bitgen.xwbt $(TARGET)_guide.ncd $(TARGET)_map.map $(TARGET)_map.mrp $(TARGET)_map.ncd $(TARGET)_map.ngm $(TARGET)_map.xrpt $(TARGET)_ngdbuild.xrpt $(TARGET)_pad.csv $(TARGET)_pad.txt $(TARGET)_par.xrpt $(TARGET)_summary.xml $(TARGET)_usage.xml $(TARGET)_xst.xrpt webtalk.log webtalk_pn.xml run.tcl $(PROJECT).gise
clean:
rm -f $(PROJECT).xise $(ISE_CRAP) *.bmm
rm -rf xst xlnx_auto_*_xdb iseconfig _xmsgs _ngo
target = "xilinx"
action = "synthesis"
syn_device = "xc6slx45t"
syn_grade = "-3"
syn_package = "fgg484"
syn_top = "cute_top"
top_module = "cute_top"
syn_project = "cute_top_wrc.xise"
modules = {
"local" : [
"../../../submodules/wr-cores/top/cute_wr/wr_core_demo"
]
}
PREFIX ?= /usr/local
STAGING ?=
EB ?= ../submodules/wr-cores/ip_cores/etherbone-core/api
TARGETS := eb-flash eb-info eb-console
EXTRA_FLAGS ?=
CFLAGS ?= $(EXTRA_FLAGS) -Wall -O2 -I $(EB)
LIBS ?= -L $(EB) -Wl,-rpath,$(PREFIX)/lib -letherbone
all: $(TARGETS)
clean:
rm -f $(TARGETS)
install:
mkdir -p $(STAGING)$(PREFIX)/bin
cp $(TARGETS) $(STAGING)$(PREFIX)/bin
%: %.c
gcc $(CFLAGS) -o $@ $< $(LIBS)
%: %.cpp
g++ $(CFLAGS) -o $@ $< $(LIBS) -leca -ltlu
/** @file eb-info.c
* @brief Report the contents of an FPGA using Etherbone.
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* A complete skeleton of an application using the Etherbone library.
*
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
#define _XOPEN_SOURCE 500 /* strtoull, usleep, tcgetattr */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <etherbone.h>
#include <unistd.h> /* getopt */
#include <termios.h>
#include <fcntl.h>
#define CERN_ID 0xce42
#define UART_ID 0xe2d13d04
#define VUART_TX 0x10
#define VUART_RX 0x14
#define STDIN_FD 0
#define BATCH_SIZE 200
const char *program;
static void help(void) {
fprintf(stderr, "Usage: %s [OPTION] <proto/host/port>\n", program);
fprintf(stderr, "\n");
fprintf(stderr, " -i <index> select between multiple uarts\n");
fprintf(stderr, " -h display this help and exit\n");
fprintf(stderr, "\n");
fprintf(stderr, "Report bugs to <w.terpstra@gsi.de>\n");
}
static void die(const char* msg, eb_status_t status) {
fprintf(stderr, "%s: %s: %s\n", program, msg, eb_status(status));
exit(1);
}
int main(int argc, char** argv) {
int opt, error, c, i, flags, busy;
char *tail;
struct sdb_device sdb[10];
struct termios tc, old;
eb_status_t status;
eb_socket_t socket;
eb_device_t device;
eb_address_t tx, rx;
eb_data_t rx_data[BATCH_SIZE], tx_data, done;
eb_cycle_t cycle;
char byte;
/* Default arguments */
program = argv[0];
error = 0;
i = -1;
/* Process the command-line arguments */
error = 0;
while ((opt = getopt(argc, argv, "i:h")) != -1) {
switch (opt) {
case 'h':
help();
return 0;
case 'i':
i = strtol(optarg, &tail, 0);
if (*tail != 0) {
fprintf(stderr, "%s: specify a proper number, not '%s'!\n", program, optarg);
error = 1;
}
break;
case ':':
case '?':
error = 1;
break;
default:
fprintf(stderr, "%s: bad getopt result\n", program);
error = 1;
}
}
if (error) return 1;
if (optind + 1 != argc) {
fprintf(stderr, "%s: expecting three non-optional arguments: <proto/host/port>\n", program);
return 1;
}
if ((status = eb_socket_open(EB_ABI_CODE, 0, EB_DATAX|EB_ADDRX, &socket)) != EB_OK)
die("eb_socket_open", status);
if ((status = eb_device_open(socket, argv[optind], EB_DATAX|EB_ADDRX, 3, &device)) != EB_OK)
die(argv[optind], status);
c = sizeof(sdb)/sizeof(struct sdb_device);
if ((status = eb_sdb_find_by_identity(device, CERN_ID, UART_ID, &sdb[0], &c)) != EB_OK)
die("eb_sdb_find_by_identity", status);
if (i == -1) {
if (c != 1) {
fprintf(stderr, "%s: found %d UARTs on that device; pick one with -i #\n", program, c);
exit(1);
} else {
i = 0;
}
}
if (i >= c) {
fprintf(stderr, "%s: could not find UART #%d on that device (%d total)\n", program, i, c);
exit(1);
}
printf("Connected to uart at address %"PRIx64"\n", sdb[i].sdb_component.addr_first);
tx = sdb[i].sdb_component.addr_first + VUART_TX;
rx = sdb[i].sdb_component.addr_first + VUART_RX;
/* disable input buffering and echo */
tcgetattr(STDIN_FD, &old);
tcgetattr(STDIN_FD, &tc);
tc.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
tc.c_iflag = IGNPAR;
tc.c_oflag = 0;
tc.c_lflag = 0;
tc.c_cc[VMIN]=1;
tc.c_cc[VTIME]=0;
tcflush(STDIN_FD, TCIFLUSH);
tcsetattr(STDIN_FD, TCSANOW, &tc);
flags = fcntl(STDIN_FD, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(STDIN_FD, F_SETFL, flags);
/* be lazy and just poll for now */
busy = 0;
while (1) {
if (!busy) usleep(10000); /* 10ms */
/* Poll for status */
eb_cycle_open(device, 0, eb_block, &cycle);
eb_cycle_read(cycle, rx, EB_BIG_ENDIAN|EB_DATA32, &rx_data[0]);
eb_cycle_read(cycle, tx, EB_BIG_ENDIAN|EB_DATA32, &done);
eb_cycle_close(cycle);
/* Bulk read anything extra */
if ((rx_data[0] & 0x100) != 0) {
eb_cycle_open(device, 0, eb_block, &cycle);
for (i = 1; i < BATCH_SIZE; ++i)
eb_cycle_read(cycle, rx, EB_BIG_ENDIAN|EB_DATA32, &rx_data[i]);
eb_cycle_close(cycle);
for (i = 0; i < BATCH_SIZE; ++i) {
if ((rx_data[i] & 0x100) == 0) continue;
byte = rx_data[i] & 0xFF;
fputc(byte, stdout);
}
fflush(stdout);
}
busy = busy && (done & 0x100) == 0;
if (!busy && read(STDIN_FD, &byte, 1) == 1) {
if (byte == 3) { /* control-C */
tcsetattr(STDIN_FD, TCSANOW, &old);
exit(0);
}
tx_data = byte;
eb_device_write(device, tx, EB_BIG_ENDIAN|EB_DATA32, tx_data, eb_block, 0);
busy = 1;
}
}
return 0;
}
/** @file eb-flash.c
* @brief Program a flash device using Etherbone.
*
* Copyright (C) 2013 GSI Helmholtz Centre for Heavy Ion Research GmbH
*
* A complete skeleton of an application using the Etherbone library.
*
* @author Wesley W. Terpstra <w.terpstra@gsi.de>
*
*******************************************************************************
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
#define _POSIX_C_SOURCE 200112L /* strtoull */
#include <unistd.h> /* getopt */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <etherbone.h>
#define GSI_ID 0x651
#define FLASH_ID 0x5cf12a1c
#define MAX_SECTOR_SIZE 4194304
static const char* program;
static const char* device_address;
static const char* firmware;
static eb_address_t address;
static int address_set;
static eb_address_t erase_address;
static int erase_address_set;
static eb_width_t address_width;
static eb_width_t data_width;
static eb_format_t endian;
static eb_address_t sector_size;
static eb_address_t page_size;
static long wait_us;
static int cycles_per_poll;
static int retries;
static int full;
static int probe;
static int verify;
static int invert;
static int verbose;
static int quiet;
static int old_erase;
static eb_address_t firmware_length;
static eb_format_t format;
static eb_data_t ffs;
static eb_data_t mask_0f;
static uint8_t* erase_bitmap;
static int erase_bitmap_size;
static FILE* firmware_f;
static void help(void) {
fprintf(stderr, "Usage: %s [OPTION] <proto/host/port> <firmware>\n", program);
fprintf(stderr, "\n");
fprintf(stderr, " -t <address> target flash base address (sdb-auto-located)\n");
fprintf(stderr, " -e <address> erase control register address (end-of-device)\n");
fprintf(stderr, " -a <width> acceptable address bus widths (8/16/32/64)\n");
fprintf(stderr, " -d <width> acceptable data bus widths (8/16/32/64)\n");
fprintf(stderr, " -b big-endian operation (auto)\n");
fprintf(stderr, " -l little-endian operation (auto)\n");
fprintf(stderr, " -i <0/1> invert bit order of bytes in firmware (auto)\n");
fprintf(stderr, " -s <bytes> flash sector size (auto)\n");
fprintf(stderr, " -x <bytes> flash page size (256)\n");
fprintf(stderr, " -w <seconds> poll interval while erasing (0.1)\n");
fprintf(stderr, " -c <cycles> number of cycles per bridge access (4)\n");
fprintf(stderr, " -r <retries> number of times to attempt autonegotiation (3)\n");
fprintf(stderr, " -f full; skip quick scan and erase everything\n");
fprintf(stderr, " -p disable self-describing wishbone device probe\n");
fprintf(stderr, " -n do not verify contents after programming\n");
fprintf(stderr, " -v verbose operation\n");
fprintf(stderr, " -q quiet: do not display warnings\n");
fprintf(stderr, " -h display this help and exit\n");
fprintf(stderr, "\n");
fprintf(stderr, "Report bugs to <w.terpstra@gsi.de>\n");
}
static int bitmask_get(int offset) {
return (erase_bitmap[offset/8] >> (offset&7)) & 1;
}
static void bitmask_set(int offset, int value) {
uint8_t* val = &erase_bitmap[offset/8];
int bit = 1 << (offset&7);
*val = (*val&~bit)|(value?bit:0);
}
static eb_data_t flip_bits(eb_data_t x) {
eb_data_t mask;
if (!invert) return x;
/* 0x0F0F */
mask = mask_0f;;
x = ((x >> 4) & mask) | ((x & mask) << 4);
/* 0x0F0F -> 0x3333 */
mask ^= (mask << 2);
x = ((x >> 2) & mask) | ((x & mask) << 2);
/* 0x3333 -> 0x5555 */
mask ^= (mask << 1);
x = ((x >> 1) & mask) | ((x & mask) << 1);
return x;
}
static eb_status_t erase_sector(eb_device_t device, eb_address_t sector) {
eb_cycle_t cycle;
eb_format_t control;
eb_status_t status;
control = (format&EB_ENDIAN_MASK) | EB_DATA32;
if (old_erase)
return eb_device_write(device, erase_address, control, sector, 0, eb_block);
if ((status = eb_cycle_open(device, 0, eb_block, &cycle)) != EB_OK) return status;
eb_cycle_write(cycle, erase_address, control, 0x6); /* write enable */
eb_cycle_write(cycle, erase_address, control, 0x80000000); /* execute */
eb_cycle_write(cycle, erase_address, control, 0xD8); /* sector erase */
if (((erase_address-address) >> 24) != 0)
eb_cycle_write(cycle, erase_address, control, (sector >> 24) & 0xFF);
eb_cycle_write(cycle, erase_address, control, (sector >> 16) & 0xFF);
eb_cycle_write(cycle, erase_address, control, (sector >> 8) & 0xFF);
eb_cycle_write(cycle, erase_address, control, (sector >> 0) & 0xFF);
eb_cycle_write(cycle, erase_address, control, 0x80000000); /* execute */
return eb_cycle_close(cycle);
}
static void wait_for_busy(eb_device_t device, long interval) {
long delay, used;
eb_status_t status;
eb_data_t result;
do {
/* Sleep as requested */
for (delay = interval; delay > 0; delay -= used) {
used = eb_socket_run(eb_device_socket(device), delay);
}
result = 0;
status = eb_device_read(device, erase_address, (format&EB_ENDIAN_MASK)|EB_DATA32, &result, 0, eb_block);
if (status != EB_OK && status != EB_SEGFAULT) {
fprintf(stderr, "%s: polling erase status failed: %s\n", program, eb_status(status));
exit(1);
}
} while (status == EB_SEGFAULT);
}
static void find_sector_size(eb_user_data_t user, eb_device_t dev, eb_operation_t op, eb_status_t status) {
eb_address_t* sector_size = (eb_address_t*)user;
eb_address_t result;
if (status != EB_OK) {
fprintf(stderr, "%s: pattern read failed: %s\n", program, eb_status(status));
exit(1);
}
result = page_size;
for (; op != EB_NULL; op = eb_operation_next(op)) {
if (eb_operation_had_error(op)) {
fprintf(stderr, "%s: wishbone segfault reading %s %s bits from address 0x%"EB_ADDR_FMT"\n",
program,
eb_width_data(eb_operation_format(op)),
eb_format_endian(eb_operation_format(op)),
eb_operation_address(op));
exit(1);
}
/* If the data is 0, then it was erased */
if (eb_operation_data(op) != 0) {
result <<= 1;
} else {
break;
}
}
*sector_size = result;
}
static eb_address_t detect_sector_size(eb_device_t device) {
eb_address_t sector_size;
eb_address_t max_size;
eb_address_t target;
eb_address_t end;
eb_status_t status;
eb_cycle_t cycle;
/* Plan:
* 0- Find largest possible sector that fits between start and end
* 1- Write 0s at positions 2^x-1 inside that sector
* 2- Erase the start address of the sector
* 3- Read the values at all the addresses that were zeroed.
* 4- The last non-zero seen determines the sector size
*/
if (!quiet) {
printf("Autodetecting sector size ... ");
fflush(stdout);
}
/* Find largest sector that fits by aligning target */
end = address + firmware_length-1;
target = address + firmware_length/2;
sector_size = page_size;
target &= ~(sector_size-1);
while (address <= target && target+sector_size-1 <= end) {
sector_size <<= 1;
target &= ~(sector_size-1);
if (sector_size >= MAX_SECTOR_SIZE) break; /* faster */
}
max_size = sector_size >> 1;
target = (address + firmware_length/2) & ~(max_size-1);
/* Start writing test pattern */
for (sector_size = page_size; sector_size < max_size; sector_size <<= 1) {
if (!quiet) {
printf("\rAutodetecting sector size: write 0x%"EB_ADDR_FMT" ... ", target+sector_size);
fflush(stdout);
}
status = eb_device_write(device, target+sector_size, format, 0, 0, eb_block);
if (status != EB_OK) {
fprintf(stderr, "\r%s: failed to write test pattern at 0x%"EB_ADDR_FMT": %s\n", program, target+sector_size, eb_status(status));
exit(1);
}
wait_for_busy(device, 0);
}
if (!quiet) {
printf("\rAutodetecting sector size: erasing 0x%"EB_ADDR_FMT" ... ", target);
fflush(stdout);
}
if ((status = erase_sector(device, target)) != EB_OK) {
fprintf(stderr, "\r%s: failed to erase test sector 0x%"EB_ADDR_FMT": %s\n", program, target, eb_status(status));
exit(1);
}
wait_for_busy(device, wait_us);
if (!quiet) {
printf("\rAutodetecting sector size: scanning ... ");
fflush(stdout);
}
if ((status = eb_cycle_open(device, &sector_size, find_sector_size, &cycle)) != EB_OK) {
fprintf(stderr, "\r%s: could not create cycle: %s\n", program, eb_status(status));
exit(1);
}
for (sector_size = page_size; sector_size < max_size; sector_size <<= 1) {
eb_cycle_read(cycle, target+sector_size, format, 0);
}
sector_size = 0;
eb_cycle_close(cycle);
while (sector_size == 0) {
eb_socket_run(eb_device_socket(device), -1);
}
if (!quiet) {
printf("\rAutodetecting sector size: found = 0x%"EB_ADDR_FMT"\n", sector_size);
}
return sector_size;
}
static void detect_decrement(eb_user_data_t user, eb_device_t dev, eb_operation_t op, eb_status_t status) {
int* counter = (int*)user;
--*counter;
if (status != EB_OK) {
fprintf(stderr, "%s: scan operation failed: %s\n", program, eb_status(status));
exit(1);
}
for (; op != EB_NULL; op = eb_operation_next(op)) {
if (eb_operation_had_error(op)) {
fprintf(stderr, "%s: wishbone segfault reading %s %s bits from address 0x%"EB_ADDR_FMT"\n",
program,
eb_width_data(eb_operation_format(op)),
eb_format_endian(eb_operation_format(op)),
eb_operation_address(op));
exit(1);
}
/* If the data is not FFFFFF, then set the bit indicating we must erase it */
if (eb_operation_data(op) != ffs) {
bitmask_set((eb_operation_address(op) - address) / sector_size, 1);
}
}
}
static int scan_count = 0;
static void quick_scan_gap(eb_device_t device, eb_address_t offset, eb_address_t len) {
eb_address_t sector;
eb_address_t first_sector;
eb_address_t last_sector;
eb_address_t position;
eb_address_t last_position;
eb_status_t status;
eb_cycle_t cycle;
int i;
int size;
int ops;
int inflight;
size = format & EB_DATAX;
ops = 0;
inflight = 0;
if ((status = eb_cycle_open(device, &inflight, detect_decrement, &cycle)) != EB_OK) {
fprintf(stderr, "\r%s: could not create cycle: %s\n", program, eb_status(status));
}
first_sector = (address/sector_size) * sector_size;
last_sector = ((address+firmware_length+sector_size-1)/sector_size)*sector_size;
i = 0;
for (sector = first_sector; sector != last_sector; sector += sector_size) {
if (bitmask_get(i++)) continue;
last_position = sector+offset+len;
for (position = sector+offset; position != last_position; position += size) {
eb_cycle_read(cycle, position, format, 0);
if (!quiet && (++scan_count % 65536) == 0) {
scan_count = 0;
printf("\rScanning offset 0x%"EB_ADDR_FMT" ... ", position);
fflush(stdout);
}
/* use the same number of operations per cycle as programming/verifying */
if (++ops == (page_size/size)) {
ops = 0;
++inflight;
eb_cycle_close(cycle);
if ((status = eb_cycle_open(device, &inflight, detect_decrement, &cycle)) != EB_OK) {
fprintf(stderr, "\r%s: could not create cycle: %s\n", program, eb_status(status));
}
while (inflight >= cycles_per_poll)
eb_socket_run(eb_device_socket(device), -1);
}
}
}
if (ops > 0) {