mirror of
https://github.com/keirf/greaseweazle-firmware.git
synced 2025-10-31 11:06:44 -07:00
bandwidth: Calculate Min / Mean / Max bandwidth in each direction
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
95
src/floppy.c
95
src/floppy.c
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user