Przeglądaj źródła

usb: Define new astable write region and use it to move NFA logic onto the host.

The write protocol handling is reworked on the firmware side.
Keir Fraser 4 lat temu
rodzic
commit
419df4825a
3 zmienionych plików z 128 dodań i 19 usunięć
  1. 21 0
      inc/cdc_acm_protocol.h
  2. 11 1
      scripts/greaseweazle/usb.py
  3. 96 18
      src/floppy.c

+ 21 - 0
inc/cdc_acm_protocol.h

@@ -97,9 +97,30 @@
 
 /*
  * Flux stream opcodes. Preceded by 0xFF byte.
+ * 
+ * Argument types:
+ *  N28: 28-bit non-negative integer N, encoded as 4 bytes b0,b1,b2,b3:
+ *   b0 = (uint8_t)(1 | (N <<  1))
+ *   b1 = (uint8_t)(1 | (N >>  6))
+ *   b2 = (uint8_t)(1 | (N >> 13))
+ *   b3 = (uint8_t)(1 | (N >> 20))
  */
+/* FLUXOP_INDEX [CMD_READ_FLUX]
+ *  Args:
+ *   +4 [N28]: ticks to index, relative to sample cursor.
+ *  Signals an index pulse in the read stream. Sample cursor is unaffected. */
 #define FLUXOP_INDEX      1
+/* FLUXOP_SPACE [CMD_READ_FLUX, CMD_WRITE_FLUX]
+ *  Args:
+ *   +4 [N28]: ticks to increment the sample cursor.
+ *  Increments the sample cursor with no intervening flux transitions. */
 #define FLUXOP_SPACE      2
+/* FLUXOP_ASTABLE [CMD_WRITE_FLUX]
+ *  Args:
+ *   +4 [N28]: astable period.
+ *  Generate regular flux transitions at specified astable period. 
+ *  Duration is specified by immediately preceding FLUXOP_SPACE opcode(s). */
+#define FLUXOP_ASTABLE    3
 
 
 /*

+ 11 - 1
scripts/greaseweazle/usb.py

@@ -113,6 +113,7 @@ class BusType:
 class FluxOp:
     Index           = 1
     Space           = 2
+    Astable         = 3
 
 
 ## CmdError: Encapsulates a command acknowledgement.
@@ -303,6 +304,8 @@ class Unit:
     ## _encode_flux:
     ## Convert the given flux timings into an encoded data stream.
     def _encode_flux(self, flux):
+        nfa_thresh = round(150e-6 * self.sample_freq)  # 150us
+        nfa_period = round(1.25e-6 * self.sample_freq) # 1.25us
         dat = bytearray()
         def _write_28bit(x):
             dat.append(1 | (x<<1) & 255)
@@ -314,6 +317,13 @@ class Unit:
                 pass
             elif val < 250:
                 dat.append(val)
+            elif val > nfa_thresh:
+                dat.append(255)
+                dat.append(FluxOp.Space)
+                _write_28bit(val)
+                dat.append(255)
+                dat.append(FluxOp.Astable)
+                _write_28bit(nfa_period)
             else:
                 high = (val-250) // 255
                 if high < 5:
@@ -322,7 +332,7 @@ class Unit:
                 else:
                     dat.append(255)
                     dat.append(FluxOp.Space)
-                    _write_28bit(val - 249);
+                    _write_28bit(val - 249)
                     dat.append(249)
         dat.append(0) # End of Stream
         return dat

+ 96 - 18
src/floppy.c

@@ -298,9 +298,14 @@ static struct {
     bool_t packet_ready;
     bool_t write_finished;
     bool_t terminate_at_index;
-    bool_t no_flux_area;
+    uint32_t astable_period;
     unsigned int packet_len;
-    uint32_t ticks_since_flux;
+    uint32_t ticks;
+    enum {
+        FLUXMODE_idle,    /* generating no flux (awaiting next command) */
+        FLUXMODE_oneshot, /* generating a single flux */
+        FLUXMODE_astable  /* generating a region of oscillating flux */
+    } flux_mode;
     uint8_t packet[USB_HS_MPS];
 } rw;
 
@@ -507,24 +512,62 @@ static uint32_t _read_28bit(void)
 
 static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
 {
+#define MIN_PULSE sample_ns(800)
+
     unsigned int todo = nr;
-    uint32_t x, ticks = rw.ticks_since_flux;
+    uint32_t x, ticks = rw.ticks;
 
     if (todo == 0)
         return 0;
 
-    if (rw.no_flux_area) {
-        unsigned int nfa_pulse = sample_ns(1250);
-        while (ticks >= nfa_pulse) {
-            *tbuf++ = nfa_pulse - 1;
-            ticks -= nfa_pulse;
+    switch (rw.flux_mode) {
+
+    case FLUXMODE_astable: {
+        /* Produce flux transitions at the specified period. */
+        uint32_t pulse = rw.astable_period;
+        while (ticks >= pulse) {
+            *tbuf++ = pulse - 1;
+            ticks -= pulse;
             if (!--todo)
                 goto out;
         }
-        rw.no_flux_area = FALSE;
+        rw.flux_mode = FLUXMODE_idle;
+        break;
+    }
+
+    case FLUXMODE_oneshot:
+        /* If ticks to next flux would overflow the hardware counter, insert
+         * extra fluxes as necessary to get us to the proper next flux. */
+        while (ticks != (timcnt_t)ticks) {
+            uint32_t pulse = (timcnt_t)-1 + 1;
+            *tbuf++ = pulse - 1;
+            ticks -= pulse;
+            if (!--todo)
+                goto out;
+        }
+
+        /* Process the one-shot unless it's too short, in which case
+         * it will be merged into the next region. */
+        if (ticks > MIN_PULSE) {
+            *tbuf++ = ticks - 1;
+            ticks = 0;
+            if (!--todo)
+                goto out;
+        }
+
+        rw.flux_mode = FLUXMODE_idle;
+        break;
+
+    case FLUXMODE_idle:
+        /* Nothing to do (waiting for a flux command). */
+        break;
+
     }
 
     while (u_cons != u_prod) {
+
+        ASSERT(rw.flux_mode == FLUXMODE_idle);
+
         x = u_buf[U_MASK(u_cons)];
         if (x == 0) {
             /* 0: Terminate */
@@ -532,10 +575,10 @@ static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
             rw.write_finished = TRUE;
             goto out;
         } else if (x < 250) {
-            /* 1-249: One byte */
+            /* 1-249: One byte. Time to next flux.*/
             u_cons++;
         } else if (x < 255) {
-            /* 250-254: Two bytes */
+            /* 250-254: Two bytes. Time to next flux. */
             if ((uint32_t)(u_prod - u_cons) < 2)
                 goto out;
             u_cons++;
@@ -543,19 +586,43 @@ static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
             x += u_buf[U_MASK(u_cons++)] - 1;
         } else {
             /* 255: Six bytes */
+            uint8_t op;
             if ((uint32_t)(u_prod - u_cons) < 6)
                 goto out;
-            u_cons += 2; /* skip 255, FLUXOP_SPACE */
-            ticks += _read_28bit();
-            continue;
+            op = u_buf[U_MASK(u_cons+1)];
+            u_cons += 2;
+            switch (op) {
+            case FLUXOP_SPACE:
+                ticks += _read_28bit();
+                continue;
+            case FLUXOP_ASTABLE:
+                rw.astable_period = _read_28bit();
+                if ((rw.astable_period < MIN_PULSE)
+                    || (rw.astable_period != (timcnt_t)rw.astable_period)) {
+                    /* Bad period value: underflow or overflow. */
+                    goto error;
+                }
+                rw.flux_mode = FLUXMODE_astable;
+                goto out;
+            default:
+                /* Invalid opcode */
+                u_cons += 4;
+                goto error;
+            }
         }
 
+        /* We're now implicitly in FLUXMODE_oneshot, but we don't register it 
+         * explicitly as we usually switch straight back to FLUXMODE_idle. */
         ticks += x;
-        if (ticks < sample_ns(800))
+
+        /* This sample too small? Then ignore this flux transition. */
+        if (ticks < MIN_PULSE)
             continue;
 
-        if (ticks > sample_us(150)) {
-            rw.no_flux_area = TRUE;
+        /* This sample overflows the hardware timer's counter width?
+         * Then bail, and we'll split it into chunks. */
+        if (ticks != (timcnt_t)ticks) {
+            rw.flux_mode = FLUXMODE_oneshot;
             goto out;
         }
 
@@ -566,8 +633,14 @@ static unsigned int _wdata_decode_flux(timcnt_t *tbuf, unsigned int nr)
     }
 
 out:
-    rw.ticks_since_flux = ticks;
+    rw.ticks = ticks;
     return nr - todo;
+
+error:
+    floppy_flux_end();
+    rw.status = ACK_BAD_COMMAND;
+    floppy_state = ST_write_flux_drain;
+    goto out;
 }
 
 static void wdata_decode_flux(void)
@@ -634,6 +707,7 @@ static uint8_t floppy_write_prep(const struct gw_write_flux *wf)
 
     floppy_state = ST_write_flux_wait_data;
     memset(&rw, 0, sizeof(rw));
+    rw.flux_mode = FLUXMODE_idle;
     rw.status = ACK_OKAY;
 
     rw.terminate_at_index = wf->terminate_at_index;
@@ -648,6 +722,8 @@ static void floppy_write_wait_data(void)
 
     floppy_process_write_packet();
     wdata_decode_flux();
+    if (rw.status != ACK_OKAY)
+        return;
 
     /* We don't wait for the massive F7 u_buf[] to fill at Full Speed. */
     u_buf_threshold = ((U_BUF_SZ > 16384) && !usb_is_highspeed())
@@ -727,6 +803,8 @@ static void floppy_write(void)
 
     floppy_process_write_packet();
     wdata_decode_flux();
+    if (rw.status != ACK_OKAY)
+        return;
 
     /* Early termination on index pulse? */
     if (rw.terminate_at_index && (index.count != 0))