瀏覽代碼

bandwidth: New bandwidth measurement command
Currently measures only average bandwidth. We really care about
mimimum bandwidth, so adding that is a todo.

Keir Fraser 4 年之前
父節點
當前提交
73c3902dce
共有 5 個文件被更改,包括 177 次插入3 次删除
  1. 15 1
      inc/cdc_acm_protocol.h
  2. 57 0
      scripts/greaseweazle/tools/bandwidth.py
  3. 28 1
      scripts/greaseweazle/usb.py
  4. 1 1
      scripts/gw.py
  5. 76 0
      src/floppy.c

+ 15 - 1
inc/cdc_acm_protocol.h

@@ -55,7 +55,11 @@
 #define CMD_RESET          16
 /* CMD_ERASE_FLUX, length=6. Argument is gw_erase_flux. */
 #define CMD_ERASE_FLUX     17
-#define CMD_MAX            17
+/* CMD_SOURCE_BYTES, length=6. Argument is gw_source_bytes. */
+#define CMD_SOURCE_BYTES   18
+/* CMD_SINK_BYTES, length=6. Argument is gw_sink_bytes. */
+#define CMD_SINK_BYTES     19
+#define CMD_MAX            19
 
 
 /*
@@ -120,6 +124,16 @@ 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 {
+    uint32_t nr_bytes;
+};
+
 /* CMD_{GET,SET}_PARAMS, index 0 */
 #define PARAMS_DELAYS 0
 struct packed gw_delay {

+ 57 - 0
scripts/greaseweazle/tools/bandwidth.py

@@ -0,0 +1,57 @@
+# greaseweazle/tools/bandwidth.py
+#
+# Greaseweazle control script: Measure USB bandwidth.
+#
+# Written & released by Keir Fraser <keir.xen@gmail.com>
+#
+# This is free and unencumbered software released into the public domain.
+# See the file COPYING for more details, or visit <http://unlicense.org>.
+
+import sys, argparse
+
+from timeit import default_timer as timer
+
+from greaseweazle.tools import util
+from greaseweazle import usb as USB
+
+def measure_bandwidth(usb, args):
+    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)
+
+    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)
+
+    twobyte_us = 249/72 # Smallest time requiring a 2-byte transmission code
+    min_bw = 16 / twobyte_us # Bandwidth (Mbps) to transmit above time
+    print("Minimum *consistent* bandwidth required: %.3f Mbps" % min_bw)
+
+def main(argv):
+
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+    parser.add_argument("device", nargs="?", default="auto",
+                        help="serial device")
+    parser.prog += ' ' + argv[1]
+    args = parser.parse_args(argv[2:])
+
+    try:
+        usb = util.usb_open(args.device)
+        measure_bandwidth(usb, args)
+    except USB.CmdError as error:
+        print("Command Failed: %s" % error)
+
+
+if __name__ == "__main__":
+    main(sys.argv)
+
+# Local variables:
+# python-indent: 4
+# End:

+ 28 - 1
scripts/greaseweazle/usb.py

@@ -36,6 +36,8 @@ class Cmd:
     SetPin          = 15
     Reset           = 16
     EraseFlux       = 17
+    SourceBytes     = 18
+    SinkBytes       = 19
     str = {
         GetInfo: "GetInfo",
         Update: "Update",
@@ -54,7 +56,9 @@ class Cmd:
         SetBusType: "SetBusType",
         SetPin: "SetPin",
         Reset: "Reset",
-        EraseFlux: "EraseFlux"
+        EraseFlux: "EraseFlux",
+        SourceBytes: "SourceBytes",
+        SinkBytes: "SinkBytes"
     }
 
 
@@ -382,6 +386,29 @@ class Unit:
         self._send_cmd(struct.pack("2B", Cmd.GetFluxStatus, 2))
 
 
+    ## source_bytes:
+    ## Command Greaseweazle to source 'nr' garbage bytes.
+    def source_bytes(self, nr):
+        self._send_cmd(struct.pack("<2BI", Cmd.SourceBytes, 6, nr))
+        while nr > 0:
+            self.ser.read(1)
+            waiting = self.ser.in_waiting
+            self.ser.read(waiting)
+            nr -= 1 + waiting
+
+
+    ## sink_bytes:
+    ## Command Greaseweazle to sink 'nr' garbage bytes.
+    def sink_bytes(self, nr):
+        self._send_cmd(struct.pack("<2BI", Cmd.SinkBytes, 6, nr))
+        dat = bytes(1024*1024)
+        while nr > len(dat):
+            self.ser.write(dat)
+            nr -= len(dat)
+        self.ser.write(dat[:nr])
+        self.ser.read(1) # Sync with Greaseweazle
+
+
     ##
     ## Delay-property public getters and setters:
     ##  select_delay:      Delay (usec) after asserting drive select

+ 1 - 1
scripts/gw.py

@@ -38,7 +38,7 @@ For installation instructions please read the wiki:
           % ', '.join(missing_modules))
     sys.exit(1)
 
-actions = [ 'read', 'write', 'delays', 'update', 'pin', 'reset' ]
+actions = [ 'read', 'write', 'delays', 'update', 'pin', 'reset', 'bandwidth' ]
 argv = sys.argv
 
 if len(argv) < 2 or argv[1] not in actions:

+ 76 - 0
src/floppy.c

@@ -84,6 +84,8 @@ static enum {
     ST_write_flux,
     ST_write_flux_drain,
     ST_erase_flux,
+    ST_source_bytes,
+    ST_sink_bytes,
 } floppy_state = ST_inactive;
 
 static uint8_t u_buf[8192];
@@ -771,6 +773,54 @@ static void floppy_erase(void)
     floppy_end_command(u_buf, 1);
 }
 
+
+/*
+ * SINK/SOURCE
+ */
+
+/* Reuse space in the R/W state structure. */
+#define ss_todo rw.ticks_since_flux
+
+static void source_bytes(void)
+{
+    if (!ep_tx_ready(EP_TX))
+        return;
+
+    if (ss_todo < USB_FS_MPS) {
+        floppy_state = ST_command_wait;
+        floppy_end_command(rw.packet, ss_todo);
+        return; /* FINISHED */
+    }
+
+    usb_write(EP_TX, rw.packet, USB_FS_MPS);
+    ss_todo -= USB_FS_MPS;
+}
+
+static void sink_bytes(void)
+{
+    int len;
+
+    if (ss_todo == 0) {
+        /* We're done: Wait for space to write the ACK byte. */
+        if (!ep_tx_ready(EP_TX))
+            return;
+        u_buf[0] = ACK_OKAY;
+        floppy_state = ST_command_wait;
+        floppy_end_command(u_buf, 1);
+        return; /* FINISHED */
+    }
+
+    /* Packet ready? */
+    len = ep_rx_ready(EP_RX);
+    if (len < 0)
+        return;
+
+    /* Read it and adjust byte counter. */
+    usb_read(EP_RX, rw.packet, len);
+    ss_todo = (ss_todo <= len) ? 0 : ss_todo - len;
+}
+
+
 static void process_command(void)
 {
     uint8_t cmd = u_buf[0];
@@ -908,6 +958,24 @@ 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_SINK_BYTES: {
+        struct gw_sink_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_sink_bytes;
+        break;
+    }
     case CMD_SWITCH_FW_MODE: {
         uint8_t mode = u_buf[2];
         if ((len != 3) || (mode & ~1))
@@ -1006,6 +1074,14 @@ void floppy_process(void)
         floppy_erase();
         break;
 
+    case ST_source_bytes:
+        source_bytes();
+        break;
+
+    case ST_sink_bytes:
+        sink_bytes();
+        break;
+
     default:
         break;