ソースを参照

floppy: Simplify the read protocol a bit.

Keir Fraser 5 年 前
コミット
b37c5ceaa3
4 ファイル変更158 行追加123 行削除
  1. 3 3
      inc/cdc_acm_protocol.h
  2. 117 47
      scripts/gw.py
  3. 36 71
      src/floppy.c
  4. 2 2
      src/fw_update.c

+ 3 - 3
inc/cdc_acm_protocol.h

@@ -27,8 +27,8 @@
 #define CMD_WRITE_FLUX      7
 /* CMD_GET_FLUX_STATUS, length=2. Last read/write status returned in ACK. */
 #define CMD_GET_FLUX_STATUS 8
-/* CMD_GET_READ_INFO, length=2. Returns 7*8 bytes after ACK. */
-#define CMD_GET_READ_INFO   9
+/* CMD_GET_INDEX_TIMES, length=2. Returns 15*4 bytes after ACK. */
+#define CMD_GET_INDEX_TIMES 9
 
 /* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>. 
  * Host follows with <update_len> bytes.
@@ -48,7 +48,7 @@
 struct __packed gw_info {
     uint8_t fw_major;
     uint8_t fw_minor;
-    uint8_t max_rev;
+    uint8_t max_index;
     uint8_t max_cmd;
     uint32_t sample_freq;
 };

+ 117 - 47
scripts/gw.py

@@ -28,7 +28,7 @@ CMD_MOTOR           = 5
 CMD_READ_FLUX       = 6
 CMD_WRITE_FLUX      = 7
 CMD_GET_FLUX_STATUS = 8
-CMD_GET_READ_INFO   = 9
+CMD_GET_INDEX_TIMES = 9
 
 # Bootloader-specific:
 CMD_UPDATE          = 1
@@ -68,9 +68,9 @@ def get_fw_info():
   return x
 
 def print_fw_info(info):
-  (major, minor, max_revs, max_cmd, freq) = info
+  (major, minor, max_index, max_cmd, freq) = info
   print("Greaseweazle v%u.%u" % (major, minor))
-  print("Max revs %u" % (max_revs))
+  print("Max index timings %u" % (max_index))
   print("Max cmd %u" % (max_cmd))
   print("Sample frequency: %.2f MHz" % (freq / 1000000))
   
@@ -102,17 +102,20 @@ def set_delays(step_delay = None, seek_settle = None,
 def motor(state):
   send_cmd(struct.pack("3B", CMD_MOTOR, 3, int(state)))
 
-def get_read_info():
-  send_cmd(struct.pack("2B", CMD_GET_READ_INFO, 2))
+def get_index_times():
+  send_cmd(struct.pack("2B", CMD_GET_INDEX_TIMES, 2))
   x = []
-  for i in range(7):
-    x.append(struct.unpack("<2I", ser.read(2*4)))
+  for i in range(15):
+    x.append(struct.unpack("<I", ser.read(4))[0])
   return x
 
-def print_read_info(info):
-  for (time, samples) in info:
-    print("%u ticks, %u samples" % (time, samples))
+def print_index_times(index_times):
+  for ticks in index_times:
+    print("%u ticks" % (ticks))
 
+
+# write_flux:
+# Write the current track via Greaseweazle with the specified flux timings.
 def write_flux(flux):
   start = timer()
   x = bytearray()
@@ -152,13 +155,18 @@ def write_flux(flux):
     end = timer()
     #print("Track written in %f seconds" % (end-start))
     break
-  
-def read_flux(nr_revs):
+
+
+# read_flux:
+# Read flux timings from Greaseweazle for the current track.
+def read_flux(nr_idx):
+
+  # Read the encoded flux stream, retrying if necessary.
   retry = 0
   while True:
     start = timer()
     x = collections.deque()
-    send_cmd(struct.pack("3B", CMD_READ_FLUX, 3, nr_revs))
+    send_cmd(struct.pack("3B", CMD_READ_FLUX, 3, nr_idx))
     nr = 0
     while True:
       x += ser.read(1)
@@ -180,6 +188,7 @@ def read_flux(nr_revs):
     
   #print("Read %u bytes in %u batches in %f seconds" % (len(x), nr, end-start))
 
+  # Decode the flux stream into a list of flux samples.
   start = timer()
   y = []
   while x:
@@ -204,8 +213,78 @@ def read_flux(nr_revs):
 
   return y
 
-def read(args):
+
+# flux_to_scp:
+# Converts Greaseweazle flux samples into a Supercard Pro Track.
+# Returns the Track Data Header (TDH) and the SCP "bitcell" array.
+def flux_to_scp(flux, track, nr_revs):
+
   factor = scp_freq / sample_freq
+
+  index_times = get_index_times()[:nr_revs+1]
+  tdh = struct.pack("<3sB", b"TRK", track)
+  dat = bytearray()
+
+  len_at_index = rev = 0
+  to_index = index_times[0]
+  rem = 0.0
+
+  for x in flux:
+
+    # Are we processing initial samples before the first revolution?
+    if rev == 0:
+      if to_index >= x:
+        # Discard initial samples
+        to_index -= x
+        continue
+      # Now starting the first full revolution
+      rev = 1
+      to_index += index_times[rev]
+
+    # Does the next flux interval cross the index mark?  
+    while to_index < x:
+      # Append to the Track Data Header for the previous full revolution
+      tdh += struct.pack("<III",
+                         int(round(index_times[rev]*factor)),
+                         (len(dat) - len_at_index) // 2,
+                         4 + nr_revs*12 + len_at_index)
+      # Set up for the next revolution
+      len_at_index = len(dat)
+      rev += 1
+      if rev > nr_revs:
+        # We're done: We simply discard any surplus flux samples
+        return (tdh, dat)
+      to_index += index_times[rev]
+
+    # Process the current flux sample into SCP "bitcell" format
+    to_index -= x
+    y = x * factor + rem
+    val = int(round(y))
+    if (val & 65535) == 0:
+      val += 1
+    rem = y - val
+    while val >= 65536:
+      dat.append(0)
+      dat.append(0)
+      val -= 65536
+    dat.append(val>>8)
+    dat.append(val&255)
+
+  # Header for last track(s) in case we ran out of flux timings.
+  while rev <= nr_revs:
+    tdh += struct.pack("<III",
+                       int(round(index_times[rev]*factor)),
+                       (len(dat) - len_at_index) // 2,
+                       4 + nr_revs*12 + len_at_index)
+    len_at_index = len(dat)
+    rev += 1
+    
+  return (tdh, dat)
+
+
+# read_to_scp:
+# Reads a floppy disk and dumps it into a new Supercard Pro image file.
+def read_to_scp(args):
   trk_dat = bytearray()
   trk_offs = []
   if args.single_sided:
@@ -214,34 +293,16 @@ def read(args):
   else:
     track_range = range(args.scyl*2, (args.ecyl+1)*2)
     nr_sides = 2
-  for i in track_range:
-    cyl = i >> (nr_sides - 1)
-    side = i & (nr_sides - 1)
+  for track in track_range:
+    cyl = track >> (nr_sides - 1)
+    side = track & (nr_sides - 1)
     print("\rReading Track %u.%u..." % (cyl, side), end="")
     trk_offs.append(len(trk_dat))
     seek(cyl, side)
-    flux = read_flux(args.revs)
-    info = get_read_info()[:args.revs]
-    #print_read_info(info)
-    trk_dat += struct.pack("<3sB", b"TRK", i)
-    dat_off = 4 + args.revs*12
-    for (time, samples) in info:
-      time = int(round(time * factor))
-      trk_dat += struct.pack("<III", time, samples, dat_off)
-      dat_off += samples * 2
-    rem = 0.0
-    for x in flux:
-      y = x * factor + rem
-      val = int(round(y))
-      if (val & 65535) == 0:
-        val += 1
-      rem = y - val
-      while val >= 65536:
-        trk_dat.append(0)
-        trk_dat.append(0)
-        val -= 65536
-      trk_dat.append(val>>8)
-      trk_dat.append(val&255)
+    flux = read_flux(args.revs+1)
+    (tdh, dat) = flux_to_scp(flux, track, args.revs)
+    trk_dat += tdh
+    trk_dat += dat
   print()
   csum = 0
   for x in trk_dat:
@@ -272,7 +333,10 @@ def read(args):
     f.write(trk_offs_dat)
     f.write(trk_dat)
 
-def write(args):
+
+# write_from_scp:
+# Writes the specified Supercard Pro image file to floppy disk.
+def write_from_scp(args):
   factor = sample_freq / scp_freq
   with open(args.file, "rb") as f:
     dat = f.read()
@@ -310,7 +374,10 @@ def write(args):
     write_flux(flux)
   print()
 
-def update(args):
+
+# update_firmware:
+# Updates the Greaseweazle firmware using the specified Update File.
+def update_firmware(args):
   with open(args.file, "rb") as f:
     dat = f.read()
   (sig, maj, min, pad1, pad2, crc) = struct.unpack(">2s4BH", dat[-8:])
@@ -331,12 +398,15 @@ def update(args):
   print("Done.")
   print("** Disconnect Greaseweazle and remove the Programming Jumper.")
 
+
+# _main:
+# Argument processing and dispatch.
 def _main(argv):
 
   actions = {
-    "read" : read,
-    "write" : write,
-    "update" : update
+    "read" : read_to_scp,
+    "write" : write_from_scp,
+    "update" : update_firmware
   }
   
   parser = argparse.ArgumentParser(
@@ -349,8 +419,6 @@ def _main(argv):
   parser.add_argument("--ecyl", type=int, default=81,
                       help="last cylinder to read/write")
   parser.add_argument("--single-sided", action="store_true")
-#  parser.add_argument("--total", type=float, default=8.0,
-#                      help="total length, seconds")
   parser.add_argument("file", help="in/out filename")
   parser.add_argument("device", help="serial device")
   args = parser.parse_args(argv[1:])
@@ -406,11 +474,13 @@ def _main(argv):
   if not update_mode:
     motor(False)
 
+
 def main(argv):
   try:
     _main(argv)
   except CmdError as error:
     print("Command Failed: %s" % error)
-    
+
+
 if __name__ == "__main__":
   main(sys.argv)

+ 36 - 71
src/floppy.c

@@ -65,9 +65,7 @@ static struct {
 void IRQ_23(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI9_5 */
 static volatile struct index {
     unsigned int count;
-    time_t timestamp;
-    time_t duration;
-    unsigned int read_prod;
+    unsigned int rdata_cnt;
 } index;
 
 /* A DMA buffer for running a timer associated with a floppy-data I/O pin. */
@@ -91,7 +89,6 @@ static enum {
     ST_inactive,
     ST_command_wait,
     ST_zlp,
-    ST_read_flux_wait_index,
     ST_read_flux,
     ST_read_flux_drain,
     ST_write_flux_wait_data,
@@ -308,7 +305,7 @@ void floppy_init(void)
 }
 
 static struct gw_info gw_info = {
-    .max_rev = 7, .max_cmd = CMD_GET_READ_INFO,
+    .max_index = 15, .max_cmd = CMD_GET_INDEX_TIMES,
     .sample_freq = SYSCLK_MHZ * 1000000u
 };
 
@@ -336,60 +333,41 @@ static void floppy_end_command(void *ack, unsigned int ack_len)
 static struct {
     time_t start;
     uint8_t status;
-    uint8_t rev;
-    uint8_t nr_revs;
+    uint8_t idx, nr_idx;
     bool_t packet_ready;
     bool_t write_finished;
     unsigned int packet_len;
-    unsigned int nr_samples;
+    int ticks_since_index;
     uint32_t ticks_since_flux;
+    uint32_t index_ticks[15];
     uint8_t packet[USB_FS_MPS];
 } rw;
 
-static struct {
-    uint32_t time;
-    uint32_t samples;
-} read_info[7];
-
 static void rdata_encode_flux(void)
 {
     const uint16_t buf_mask = ARRAY_SIZE(dma.buf) - 1;
     uint16_t cons = dma.cons, prod, prev = dma.prev_sample, curr, next;
     uint32_t ticks = rw.ticks_since_flux;
-    unsigned int nr_samples;
+    int ticks_since_index = rw.ticks_since_index;
 
-    ASSERT(rw.rev < rw.nr_revs);
+    ASSERT(rw.idx < rw.nr_idx);
 
     /* We don't want to race the Index IRQ handler. */
     IRQ_global_disable();
 
     /* Find out where the DMA engine's producer index has got to. */
     prod = ARRAY_SIZE(dma.buf) - dma_rdata.cndtr;
-    nr_samples = (prod - cons) & buf_mask;
 
-    if (rw.rev != index.count) {
+    if (rw.idx != index.count) {
         /* We have just passed the index mark: Record information about 
          * the just-completed revolution. */
-        unsigned int final_samples = (index.read_prod - cons) & buf_mask;
-        if (final_samples > nr_samples) {
-            /* Only way this can happen is if the producer has overflowed 
-             * past the consumer since the Index IRQ. We will report it 
-             * back to the caller by setting an overflow on the USB ring. */
-            printk("DMA OVERFLOW\n");
-            u_prod = u_cons + sizeof(u_buf) + 1;
-            final_samples = nr_samples;
-        }
-        read_info[rw.rev].time = sysclk_stk(index.duration);
-        read_info[rw.rev].samples = rw.nr_samples + final_samples;
-        nr_samples -= final_samples;
-        rw.nr_samples = 0;
-        rw.rev++;
+        int partial_flux = ticks + (uint16_t)(index.rdata_cnt - prev);
+        rw.index_ticks[rw.idx++] = ticks_since_index + partial_flux;
+        ticks_since_index = -partial_flux;
     }
 
     IRQ_global_enable();
 
-    rw.nr_samples += nr_samples;
-
     /* Process the flux timings into the raw bitcell buffer. */
     for (; cons != prod; cons = (cons+1) & buf_mask) {
         next = dma.buf[cons];
@@ -419,6 +397,7 @@ static void rdata_encode_flux(void)
             }
         }
 
+        ticks_since_index += ticks;
         ticks = 0;
     }
 
@@ -437,9 +416,10 @@ static void rdata_encode_flux(void)
     dma.cons = cons;
     dma.prev_sample = prev;
     rw.ticks_since_flux = ticks;
+    rw.ticks_since_index = ticks_since_index;
 }
 
-static void floppy_read_prep(unsigned int revs)
+static void floppy_read_prep(unsigned int nr_idx)
 {
     /* Start DMA. */
     dma_rdata.cndtr = ARRAY_SIZE(dma.buf);
@@ -458,33 +438,16 @@ static void floppy_read_prep(unsigned int revs)
     drive_select();
     floppy_motor(TRUE);
 
-    index.count = 0;
-    floppy_state = ST_read_flux_wait_index;
-    memset(&rw, 0, sizeof(rw));
-    rw.nr_revs = revs;
-    rw.start = time_now();
-    rw.status = ACK_OKAY;
-}
-
-static void floppy_read_wait_index(void)
-{
-    if (index.count == 0) {
-        if (time_since(rw.start) > time_ms(2000)) {
-            /* Timeout */
-            printk("NO INDEX\n");
-            floppy_flux_end();
-            rw.status = ACK_NO_INDEX;
-            floppy_state = ST_read_flux_drain;
-        }
-        return;
-    }
-
     /* Start timer. */
     tim_rdata->ccer = TIM_CCER_CC2E | TIM_CCER_CC2P;
     tim_rdata->cr1 = TIM_CR1_CEN;
 
     index.count = 0;
     floppy_state = ST_read_flux;
+    memset(&rw, 0, sizeof(rw));
+    rw.nr_idx = nr_idx;
+    rw.start = time_now();
+    rw.status = ACK_OKAY;
 }
 
 static void make_read_packet(unsigned int n)
@@ -520,12 +483,22 @@ static void floppy_read(void)
             floppy_state = ST_read_flux_drain;
             u_cons = u_prod = avail = 0;
 
-        } else if (rw.rev >= rw.nr_revs) {
+        } else if (rw.idx >= rw.nr_idx) {
 
             /* Read all requested revolutions. */
             floppy_flux_end();
             floppy_state = ST_read_flux_drain;
 
+        } else if ((index.count == 0)
+                   && (time_since(rw.start) > time_ms(2000))) {
+
+            /* Timeout */
+            printk("NO INDEX\n");
+            floppy_flux_end();
+            rw.status = ACK_NO_INDEX;
+            floppy_state = ST_read_flux_drain;
+            u_cons = u_prod = avail = 0;
+
         }
 
     } else if ((avail < USB_FS_MPS)
@@ -848,10 +821,10 @@ static void process_command(void)
         break;
     }
     case CMD_READ_FLUX: {
-        uint8_t revs = u_buf[2];
+        uint8_t nr_idx = u_buf[2];
         if (len != 3) goto bad_command;
-        if ((revs == 0) || (revs > 7)) goto bad_command;
-        floppy_read_prep(revs);
+        if ((nr_idx == 0) || (nr_idx > gw_info.max_index)) goto bad_command;
+        floppy_read_prep(nr_idx);
         break;
     }
     case CMD_WRITE_FLUX: {
@@ -864,10 +837,10 @@ static void process_command(void)
         u_buf[1] = rw.status;
         goto out;
     }
-    case CMD_GET_READ_INFO: {
+    case CMD_GET_INDEX_TIMES: {
         if (len != 2) goto bad_command;
-        memcpy(&u_buf[2], &read_info, sizeof(read_info));
-        resp_sz += sizeof(read_info);
+        memcpy(&u_buf[2], rw.index_ticks, sizeof(rw.index_ticks));
+        resp_sz += sizeof(rw.index_ticks);
         break;
     }
     default:
@@ -927,10 +900,6 @@ void floppy_process(void)
         }
         break;
 
-    case ST_read_flux_wait_index:
-        floppy_read_wait_index();
-        break;
-
     case ST_read_flux:
     case ST_read_flux_drain:
         floppy_read();
@@ -969,15 +938,11 @@ const struct usb_class_ops usb_cdc_acm_ops = {
 
 static void IRQ_INDEX_changed(void)
 {
-    time_t now = time_now();
-
     /* Clear INDEX-changed flag. */
     exti->pr = m(pin_index);
 
     index.count++;
-    index.duration = time_diff(index.timestamp, now);
-    index.timestamp = now;
-    index.read_prod = ARRAY_SIZE(dma.buf) - dma_rdata.cndtr;
+    index.rdata_cnt = tim_rdata->cnt;
 }
 
 /*

+ 2 - 2
src/fw_update.c

@@ -27,8 +27,8 @@ static uint32_t u_prod;
 static bool_t pa14_strapped;
 
 static struct gw_info gw_info = {
-    /* Max Revs == 0 signals that this is the Bootloader. */
-    .max_rev = 0,
+    /* Max Index == 0 signals that this is the Bootloader. */
+    .max_index = 0,
     /* Only support two commands: GET_INFO and UPDATE. */
     .max_cmd = CMD_UPDATE,
 };