bandwidth: Calculate Min / Mean / Max bandwidth in each direction

This commit is contained in:
Keir Fraser
2020-04-27 13:52:44 +01:00
parent 73c3902dce
commit 4435650f0a
4 changed files with 113 additions and 41 deletions

View File

@@ -55,9 +55,9 @@
#define CMD_RESET 16
/* CMD_ERASE_FLUX, length=6. Argument is gw_erase_flux. */
#define CMD_ERASE_FLUX 17
/* CMD_SOURCE_BYTES, length=6. Argument is gw_source_bytes. */
/* CMD_SOURCE_BYTES, length=6. Argument is gw_sink_source_bytes. */
#define CMD_SOURCE_BYTES 18
/* CMD_SINK_BYTES, length=6. Argument is gw_sink_bytes. */
/* CMD_SINK_BYTES, length=6. Argument is gw_sink_source_bytes. */
#define CMD_SINK_BYTES 19
#define CMD_MAX 19
@@ -100,6 +100,7 @@
*/
/* CMD_GET_INFO, index 0 */
#define GETINFO_FIRMWARE 0
struct packed gw_info {
uint8_t fw_major;
uint8_t fw_minor;
@@ -109,6 +110,15 @@ struct packed gw_info {
uint16_t hw_type;
};
/* CMD_GET_INFO, index 1 */
#define GETINFO_BW_STATS 1
struct packed gw_bw_stats {
struct packed {
uint32_t bytes;
uint32_t usecs;
} min_bw, max_bw;
};
/* CMD_READ_FLUX */
struct packed gw_read_flux {
uint8_t nr_idx; /* default: 2 */
@@ -124,13 +134,8 @@ struct packed gw_erase_flux {
uint32_t erase_ticks;
};
/* CMD_SOURCE_BYTES */
struct packed gw_source_bytes {
uint32_t nr_bytes;
};
/* CMD_SINK_BYTES */
struct packed gw_sink_bytes {
/* CMD_SINK_SOURCE_BYTES */
struct packed gw_sink_source_bytes {
uint32_t nr_bytes;
};

View File

@@ -15,19 +15,25 @@ from greaseweazle.tools import util
from greaseweazle import usb as USB
def measure_bandwidth(usb, args):
print(" Min / Mean / Max")
w_nr = 1000000
start = timer()
usb.sink_bytes(w_nr)
end = timer()
w_bw = (w_nr * 8) / ((end-start) * 1000000)
print("Average Write Bandwidth: %.3f Mbps" % w_bw)
av_bw = (w_nr * 8) / ((end-start) * 1000000)
min_bw, max_bw = usb.bw_stats()
print("Write Bandwidth: %.3f / %.3f / %.3f Mbps"
% (min_bw, av_bw, max_bw))
r_nr = 1000000
start = timer()
usb.source_bytes(r_nr)
end = timer()
r_bw = (r_nr * 8) / ((end-start) * 1000000)
print("Average Read Bandwidth: %.3f Mbps" % r_bw)
av_bw = (r_nr * 8) / ((end-start) * 1000000)
min_bw, max_bw = usb.bw_stats()
print("Read Bandwidth: %.3f / %.3f / %.3f Mbps"
% (min_bw, av_bw, max_bw))
twobyte_us = 249/72 # Smallest time requiring a 2-byte transmission code
min_bw = 16 / twobyte_us # Bandwidth (Mbps) to transmit above time

View File

@@ -91,6 +91,12 @@ class Ack:
## Cmd.GetInfo indexes
class GetInfo:
Firmware = 0
BandwidthStats = 1
## Cmd.{Get,Set}Params indexes
class Params:
Delays = 0
@@ -130,7 +136,7 @@ class Unit:
self.ser = ser
self.reset()
# Copy firmware info to instance variables (see above for definitions).
self._send_cmd(struct.pack("3B", Cmd.GetInfo, 3, 0))
self._send_cmd(struct.pack("3B", Cmd.GetInfo, 3, GetInfo.Firmware))
x = struct.unpack("<4BIH22x", self.ser.read(32))
(self.major, self.minor, self.max_index,
self.max_cmd, self.sample_freq, self.hw_type) = x
@@ -409,6 +415,18 @@ class Unit:
self.ser.read(1) # Sync with Greaseweazle
## bw_stats:
## Get min/max bandwidth for previous source/sink command. Mbps (float).
def bw_stats(self):
self._send_cmd(struct.pack("3B", Cmd.GetInfo, 3,
GetInfo.BandwidthStats))
min_bytes, min_usecs, max_bytes, max_usecs = struct.unpack(
"<4I16x", self.ser.read(32))
min_bw = (8 * min_bytes) / min_usecs
max_bw = (8 * max_bytes) / max_usecs
return min_bw, max_bw
##
## Delay-property public getters and setters:
## select_delay: Delay (usec) after asserting drive select

View File

@@ -88,7 +88,8 @@ static enum {
ST_sink_bytes,
} floppy_state = ST_inactive;
static uint8_t u_buf[8192];
/* We sometimes cast u_buf to uint32_t[], hence the alignment constraint. */
static uint8_t u_buf[8192] aligned(4);
static uint32_t u_cons, u_prod;
#define U_MASK(x) ((x)&(sizeof(u_buf)-1))
@@ -778,29 +779,62 @@ static void floppy_erase(void)
* SINK/SOURCE
*/
/* Reuse space in the R/W state structure. */
#define ss_todo rw.ticks_since_flux
static struct {
unsigned int todo;
unsigned int min_delta;
unsigned int max_delta;
} ss;
static void sink_source_prep(const struct gw_sink_source_bytes *ssb)
{
ss.min_delta = INT_MAX;
ss.max_delta = 0;
ss.todo = ssb->nr_bytes;
}
static void ss_update_deltas(int len)
{
uint32_t *u_times = (uint32_t *)u_buf;
time_t delta, now = time_now();
unsigned int p = u_prod;
/* Every four bytes we store a timestamp in a u_buf[]-sized ring buffer.
* We then record min/max time taken to overwrite a previous timestamp. */
while (len--) {
if (p++ & 3)
continue;
delta = time_diff(u_times[U_MASK(p)>>2], now);
u_times[U_MASK(p)>>2] = now;
if ((delta > ss.max_delta) && (p >= sizeof(u_buf)))
ss.max_delta = delta;
if ((delta < ss.min_delta) && (p >= sizeof(u_buf)))
ss.min_delta = delta;
}
u_prod = p;
}
static void source_bytes(void)
{
if (!ep_tx_ready(EP_TX))
return;
if (ss_todo < USB_FS_MPS) {
if (ss.todo < USB_FS_MPS) {
floppy_state = ST_command_wait;
floppy_end_command(rw.packet, ss_todo);
floppy_end_command(rw.packet, ss.todo);
return; /* FINISHED */
}
usb_write(EP_TX, rw.packet, USB_FS_MPS);
ss_todo -= USB_FS_MPS;
ss.todo -= USB_FS_MPS;
ss_update_deltas(USB_FS_MPS);
}
static void sink_bytes(void)
{
int len;
if (ss_todo == 0) {
if (ss.todo == 0) {
/* We're done: Wait for space to write the ACK byte. */
if (!ep_tx_ready(EP_TX))
return;
@@ -817,7 +851,8 @@ static void sink_bytes(void)
/* Read it and adjust byte counter. */
usb_read(EP_RX, rw.packet, len);
ss_todo = (ss_todo <= len) ? 0 : ss_todo - len;
ss.todo = (ss.todo <= len) ? 0 : ss.todo - len;
ss_update_deltas(len);
}
@@ -833,12 +868,27 @@ static void process_command(void)
switch (cmd) {
case CMD_GET_INFO: {
uint8_t idx = u_buf[2];
if ((len != 3) || (idx != 0))
if (len != 3)
goto bad_command;
memset(&u_buf[2], 0, 32);
gw_info.fw_major = fw_major;
gw_info.fw_minor = fw_minor;
memcpy(&u_buf[2], &gw_info, sizeof(gw_info));
switch(idx) {
case GETINFO_FIRMWARE: /* gw_info */
gw_info.fw_major = fw_major;
gw_info.fw_minor = fw_minor;
memcpy(&u_buf[2], &gw_info, sizeof(gw_info));
break;
case GETINFO_BW_STATS: /* gw_bw_stats */ {
struct gw_bw_stats bw;
bw.min_bw.bytes = sizeof(u_buf);
bw.min_bw.usecs = ss.max_delta / time_us(1);
bw.max_bw.bytes = sizeof(u_buf);
bw.max_bw.usecs = ss.min_delta / time_us(1);
memcpy(&u_buf[2], &bw, sizeof(bw));
break;
}
default:
goto bad_command;
}
resp_sz += 32;
break;
}
@@ -958,22 +1008,15 @@ static void process_command(void)
u_buf[1] = floppy_erase_prep(&ef);
goto out;
}
case CMD_SOURCE_BYTES: {
struct gw_source_bytes sb;
if (len != (2 + sizeof(sb)))
goto bad_command;
memcpy(&sb, &u_buf[2], len-2);
ss_todo = sb.nr_bytes;
floppy_state = ST_source_bytes;
break;
}
case CMD_SOURCE_BYTES:
case CMD_SINK_BYTES: {
struct gw_sink_bytes sb;
if (len != (2 + sizeof(sb)))
struct gw_sink_source_bytes ssb;
if (len != (2 + sizeof(ssb)))
goto bad_command;
memcpy(&sb, &u_buf[2], len-2);
ss_todo = sb.nr_bytes;
floppy_state = ST_sink_bytes;
memcpy(&ssb, &u_buf[2], len-2);
floppy_state = (cmd == CMD_SOURCE_BYTES)
? ST_source_bytes : ST_sink_bytes;
sink_source_prep(&ssb);
break;
}
case CMD_SWITCH_FW_MODE: {