Per Mårtensson před 5 měsíci
rodič
revize
9448d7a2da
5 změnil soubory, kde provedl 168 přidání a 56 odebrání
  1. 2 2
      .github/workflows/ci.yml
  2. 2 25
      .github/workflows/release.yml
  3. 1 1
      Makefile
  4. 14 7
      inc/cdc_acm_protocol.h
  5. 149 21
      src/floppy.c

+ 2 - 2
.github/workflows/ci.yml

@@ -8,7 +8,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
 
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
 
     - name: Set environment variables
       id: vars
@@ -50,7 +50,7 @@ jobs:
         mv $P-$V.zip _cidist/
 
     - name: Upload artifacts
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: greaseweazle-firmware.ci.${{ steps.vars.outputs.sha_short }}
         path: _cidist

+ 2 - 25
.github/workflows/release.yml

@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
 
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
 
     - name: Set environment variables
       id: vars
@@ -26,34 +26,11 @@ jobs:
     - name: Dependency packages (pip)
       run: python3 -m pip install --user crcmod
 
-    - name: Build dist
+    - name: Build release
       run: |
         make -j4 dist
         mv out/*.zip .
 
-    - name: Upload artifacts
-      uses: actions/upload-artifact@v3
-      with:
-        name: greaseweazle-firmware.ci.${{ steps.vars.outputs.ver }}
-        path: greaseweazle-firmware-${{ steps.vars.outputs.ver }}.zip
-
-  finalise:
-    needs: build-ubuntu
-    runs-on: ubuntu-22.04
-    steps:
-
-    - uses: actions/checkout@v3
-
-    - name: Set environment variables
-      id: vars
-      run: |
-        echo "ver=$(echo ${{ github.ref }} | sed -e's#.*/v##')" >> $GITHUB_OUTPUT
-
-    - name: Download artifacts
-      uses: actions/download-artifact@v3
-      with:
-        name: greaseweazle-firmware.ci.${{ steps.vars.outputs.ver }}
-
     - name: Create Release
       id: create_release
       uses: ncipollo/release-action@v1

+ 1 - 1
Makefile

@@ -1,6 +1,6 @@
 
 export FW_MAJOR := 1
-export FW_MINOR := 5
+export FW_MINOR := 6
 
 PROJ = greaseweazle-firmware
 VER := $(FW_MAJOR).$(FW_MINOR)

+ 14 - 7
inc/cdc_acm_protocol.h

@@ -40,7 +40,7 @@
 /* CMD_READ_FLUX, length=8-12. Argument is gw_read_flux; optional fields
  * may be omitted. Returns flux readings terminating with EOStream (NUL). */
 #define CMD_READ_FLUX       7
-/* CMD_WRITE_FLUX, length=4. Argument is gw_write_flux.
+/* CMD_WRITE_FLUX, length=4-8. Argument is gw_write_flux.
  * Host follows the ACK with flux values terminating with EOStream (NUL).
  * Device finally returns a status byte, 0 on success.
  * No further commands should be issued until the status byte is received. */
@@ -145,7 +145,7 @@
  *  Generate regular flux transitions at specified astable period. 
  *  Duration is specified by immediately preceding FLUXOP_SPACE opcode(s). */
 #define FLUXOP_ASTABLE    3
-
+#define FLUXOP_HARD_INDEX      4
 
 /*
  * COMMAND PACKETS
@@ -205,6 +205,10 @@ struct packed gw_write_flux {
     uint8_t cue_at_index;
     /* If non-zero, terminate the write at the next index pulse. */
     uint8_t terminate_at_index;
+        /** OPTIONAL FIELDS: **/
+    /* Hard sector time, in ticks. Used to find first sector and to trigger
+     * cue_at_index and terminate_at_index, if they are enabled. */
+    uint32_t hard_sector_ticks; /* default: 0 (disabled) */
 };
 
 /* CMD_ERASE_FLUX */
@@ -221,11 +225,14 @@ struct packed gw_sink_source_bytes {
 /* CMD_{GET,SET}_PARAMS, index 0 */
 #define PARAMS_DELAYS 0
 struct packed gw_delay {
-    uint16_t select_delay; /* usec */
-    uint16_t step_delay;   /* usec */
-    uint16_t seek_settle;  /* msec */
-    uint16_t motor_delay;  /* msec */
-    uint16_t watchdog;     /* msec */
+    uint16_t select_delay; /* (usec) delay after asserting a drive select */
+    uint16_t step_delay;   /* (usec) delay after a head-step pulse */
+    uint16_t seek_settle;  /* (msec) delay after completing a seek operation */
+    uint16_t motor_delay;  /* (msec) delay after turning on a drive spindle */
+    uint16_t watchdog;     /* (msec) timeout after last command */
+    uint16_t pre_write;    /* (usec) min time since previous head change */
+    uint16_t post_write;   /* (usec) min time to next write/step/head-change */
+    uint16_t index_mask;   /* (usec) post-trigger index mask */
 };
 
 /* CMD_SWITCH_FW_MODE */

+ 149 - 21
src/floppy.c

@@ -28,8 +28,17 @@
 #define sample_us(x) ((x) * SAMPLE_MHZ)
 #define time_from_samples(x) udiv64((uint64_t)(x) * TIME_MHZ, SAMPLE_MHZ)
 
-#define write_pin(pin, level) \
-    gpio_write_pin(gpio_##pin, pin_##pin, level ? O_TRUE : O_FALSE)
+/* Track and modify states of output pins. */
+static struct {
+    bool_t dir;
+    bool_t step;
+    bool_t wgate;
+    bool_t head;
+} pins;
+#define read_pin(pin) pins.pin
+#define write_pin(pin, level) ({                                        \
+    gpio_write_pin(gpio_##pin, pin_##pin, level ? O_TRUE : O_FALSE);    \
+    pins.pin = level; })
 
 static int bus_type = -1;
 static int unit_nr = -1;
@@ -47,7 +56,10 @@ static const struct gw_delay factory_delay_params = {
     .step_delay = 10000,
     .seek_settle = 15,
     .motor_delay = 750,
-    .watchdog = 10000
+    .watchdog = 10000,
+    .pre_write = 100,
+    .post_write = 1000,
+    .index_mask = 200
 };
 
 extern uint8_t u_buf[];
@@ -65,13 +77,18 @@ static struct index {
     volatile unsigned int count;
     /* For synchronising index pulse reporting to the RDATA flux stream. */
     volatile unsigned int rdata_cnt;
-    /* Last time at which ISR fired. */
-    time_t isr_time;
+    /* Threshold and trigger for detecting a hard-sector index hole. */
+    uint32_t hard_sector_thresh; /* hole-to-hole threshold to detect index */
+    uint32_t hard_sector_trigger; /* != 0 -> trigger is primed */
+    /* Last time at which index was triggered. */
+    time_t trigger_time;
     /* Timer structure for index_timer() calls. */
     struct timer timer;
+    volatile time_t prev;
+    volatile unsigned int hard_count;
 } index;
 
-/* Timer to clean up stale index.isr_time. */
+/* Timer to clean up stale index.trigger_time. */
 #define INDEX_TIMER_PERIOD time_ms(5000)
 static void index_timer(void *unused);
 
@@ -130,6 +147,43 @@ static enum {
 static uint32_t u_cons, u_prod;
 #define U_MASK(x) ((x)&(U_BUF_SZ-1))
 
+static struct {
+    struct timer timer;
+    unsigned int mask;
+#define DELAY_read  (1u<<0)
+#define DELAY_write (1u<<1)
+#define DELAY_seek  (1u<<2)
+#define DELAY_head  (1u<<3)
+} op_delay;
+static void op_delay_timer(void *unused);
+
+/* Delay specified operation(s) by specified number of microseconds. */
+static void op_delay_async(unsigned int mask, unsigned int usec)
+{
+    time_t deadline;
+
+    /* Very long delays fall back to synchronous wait. */
+    if (usec > 1000000u) {
+        delay_us(usec);
+        return;
+    }
+
+    deadline = time_now() + time_us(usec);
+    timer_cancel(&op_delay.timer);
+    if ((op_delay.mask != 0) &&
+        (time_diff(op_delay.timer.deadline, deadline) < 0))
+        deadline = op_delay.timer.deadline;
+    op_delay.mask |= mask;
+    timer_set(&op_delay.timer, deadline);
+}
+
+/* Wait for specified operation(s) to be permitted. */
+static void op_delay_wait(unsigned int mask)
+{
+    while (op_delay.mask & mask)
+        cpu_relax();
+}
+
 static void drive_deselect(void)
 {
     int pin = -1;
@@ -450,7 +504,9 @@ static uint8_t floppy_seek(int cyl)
         return ACK_NO_UNIT;
     u = &unit[unit_nr];
 
-    if (!u->initialised) {
+     op_delay_wait(DELAY_seek);
+     
+     if (!u->initialised) {
         uint8_t rc = floppy_seek_initialise(u);
         if (rc != ACK_OKAY)
             return rc;
@@ -480,7 +536,8 @@ static uint8_t floppy_seek(int cyl)
 
     flippy_trk0_sensor_enable();
 
-    delay_ms(delay_params.seek_settle);
+    op_delay_async(DELAY_read | DELAY_write | DELAY_seek,
+                   delay_params.seek_settle * 1000u);
     u->cyl = cyl;
 #if MCU == STM32F7
     get_tg43_info();
@@ -514,11 +571,26 @@ static uint8_t floppy_noclick_step(void)
     return ACK_OKAY;
 }
 
+static void index_set_hard_sector_detection(uint32_t hard_sector_ticks)
+{
+    uint32_t hard_sector_time = time_from_samples(hard_sector_ticks);
+
+    IRQ_global_disable();
+    index.hard_sector_thresh = hard_sector_time * 3 / 4;
+    index.hard_sector_trigger = 0;
+    IRQ_global_enable();
+}
+
 static void floppy_flux_end(void)
 {
     /* Turn off write pins. */
-    write_pin(wgate, FALSE);
-    configure_pin(wdata, GPO_bus);    
+  
+    if (read_pin(wgate)) {
+        write_pin(wgate, FALSE);
+        configure_pin(wdata, GPO_bus);
+        op_delay_async(DELAY_write | DELAY_seek | DELAY_head,
+                       delay_params.post_write);
+    }
 
     /* Turn off timers. */
     tim_rdata->ccer = 0;
@@ -533,6 +605,9 @@ static void floppy_flux_end(void)
     dma_wdata.cr &= ~DMA_CR_EN;
     while ((dma_rdata.cr & DMA_CR_EN) || (dma_wdata.cr & DMA_CR_EN))
         continue;
+
+    /* Disable hard-sector index detection. */
+    index_set_hard_sector_detection(0);
 }
 
 static void quiesce_drives(void)
@@ -638,6 +713,9 @@ void floppy_init(void)
     IRQx_set_prio(irq_index, INDEX_IRQ_PRI);
     IRQx_enable(irq_index);
 
+    op_delay.mask = 0;
+    timer_init(&op_delay.timer, op_delay_timer, NULL);
+
     delay_params = factory_delay_params;
 
     _set_bus_type(BUS_NONE);
@@ -683,6 +761,7 @@ static struct {
     unsigned int max_index;
     uint32_t max_index_linger;
     time_t deadline;
+     unsigned int hard_nr_index;
 } read;
 
 static void _write_28bit(uint32_t x)
@@ -710,6 +789,7 @@ static void rdata_encode_flux(void)
         /* We have just passed the index mark: Record information about 
          * the just-completed revolution. */
         read.nr_index = index.count;
+        read.hard_nr_index=0;
         ticks = (timcnt_t)(index.rdata_cnt - prev);
         IRQ_global_enable(); /* we're done reading ISR variables */
         u_buf[U_MASK(u_prod++)] = 0xff;
@@ -719,7 +799,16 @@ static void rdata_encode_flux(void)
          * pulses).  */
         watchdog_kick();
     }
-
+    //Fake index 2407
+    /*
+    if (read.hard_nr_index != index.hard_count) {
+        read.hard_nr_index = index.hard_count;
+        if (read.hard_nr_index!=0){
+            u_buf[U_MASK(u_prod++)] = 0xff;
+            u_buf[U_MASK(u_prod++)] = FLUXOP_HARD_INDEX;
+        }
+    }
+    */
     IRQ_global_enable();
 
     /* Process the flux timings into the raw bitcell buffer. */
@@ -771,6 +860,7 @@ static void rdata_encode_flux(void)
 
 static uint8_t floppy_read_prep(const struct gw_read_flux *rf)
 {
+    op_delay_wait(DELAY_read);
     /* Prepare Timer & DMA. */
     dma_rdata.mar = (uint32_t)(unsigned long)dma.buf;
     dma_rdata.ndtr = ARRAY_SIZE(dma.buf);    
@@ -784,6 +874,7 @@ static uint8_t floppy_read_prep(const struct gw_read_flux *rf)
     tim_rdata->cr1 = TIM_CR1_CEN;
 
     index.count = 0;
+    index.hard_count = 0; //Per 2407
     usb_packet.ready = FALSE;
 
     floppy_state = ST_read_flux;
@@ -1117,6 +1208,8 @@ static uint8_t floppy_write_prep(const struct gw_write_flux *wf)
     write.cue_at_index = wf->cue_at_index;
     write.terminate_at_index = wf->terminate_at_index;
 
+    index_set_hard_sector_detection(wf->hard_sector_ticks);
+
     return ACK_OKAY;
 }
 
@@ -1145,6 +1238,7 @@ static void floppy_write_wait_data(void)
          || ((uint32_t)(u_prod - u_cons) < u_buf_threshold))
         && !write_finished)
         return;
+    op_delay_wait(DELAY_write);
 
     floppy_state = ST_write_flux_wait_index;
     flux_op.start = time_now();
@@ -1264,6 +1358,8 @@ static void floppy_write_drain(void)
 
 static uint8_t floppy_erase_prep(const struct gw_erase_flux *ef)
 {
+    op_delay_wait(DELAY_write);
+
     if (get_wrprot() == LOW)
         return ACK_WRPROT;
 
@@ -1281,7 +1377,7 @@ static void floppy_erase(void)
     if (time_since(flux_op.end) < 0)
         return;
 
-    write_pin(wgate, FALSE);
+    floppy_flux_end();
 
     /* ACK with Status byte. */
     u_buf[0] = flux_op.status;
@@ -1545,7 +1641,12 @@ static void process_command(void)
         uint8_t head = u_buf[2];
         if ((len != 3) || (head > 1))
             goto bad_command;
-        write_pin(head, head);
+
+        if (read_pin(head) != head) {
+            op_delay_wait(DELAY_head);
+            write_pin(head, head);
+            op_delay_async(DELAY_write, delay_params.pre_write);
+        }
         break;
     }
     case CMD_SET_PARAMS: {
@@ -1585,8 +1686,9 @@ static void process_command(void)
         goto out;
     }
     case CMD_WRITE_FLUX: {
-        struct gw_write_flux wf;
-        if (len != (2 + sizeof(wf)))
+        struct gw_write_flux wf = {};
+        if ((len < (2 + offsetof(struct gw_write_flux, hard_sector_ticks)))
+            || (len > (2 + sizeof(wf))))
             goto bad_command;
         memcpy(&wf, &u_buf[2], len-2);
         u_buf[1] = floppy_write_prep(&wf);
@@ -1806,14 +1908,33 @@ const struct usb_class_ops usb_cdc_acm_ops = {
 static void IRQ_INDEX_changed(void)
 {
     unsigned int cnt = tim_rdata->cnt;
-    time_t now = time_now(), prev = index.isr_time;
+    time_t now = time_now();
+    int32_t delta;
 
     /* Clear INDEX-changed flag. */
     exti->pr = m(pin_index);
 
-    index.isr_time = now;
-    if (time_diff(prev, now) < time_us(50))
+    delta = time_diff(index.trigger_time, now);
+    if (delta < time_us(delay_params.index_mask))
         return;
+    index.trigger_time = now;
+
+    if (unlikely(index.hard_sector_thresh != 0)) {
+        if (delta > index.hard_sector_thresh) {
+            /* Long pulse indicates a subsequent sector hole. Filter it out
+             * and unprime the index trigger. */
+            index.hard_sector_trigger = 0;
+            return;
+        }
+        /* First short pulse indicates the extra (index) hole. Second
+         * consecutive short pulse is the first sector hole: That's the only
+         * one we count. */
+        index.hard_sector_trigger ^= 1;
+        if (index.hard_sector_trigger) {
+            /* Filter out the "rising edge" of the trigger. */
+            return;
+        }
+    }
 
     index.count++;
     index.rdata_cnt = cnt;
@@ -1823,17 +1944,24 @@ static void index_timer(void *unused)
 {
     time_t now = time_now();
     IRQ_global_disable();
-    /* index.isr_time mustn't get so old that the time_diff() test in
+    /* index.trigger_time mustn't get so old that the time_diff() test in
      * IRQ_INDEX_changed() overflows. To prevent this, we ensure that,
      * at all times,
      *   time_diff(index.isr_time, time_now()) < 2*INDEX_TIMER_PERIOD + delta,
      * where delta is small. */
-    if (time_diff(index.isr_time, now) > INDEX_TIMER_PERIOD)
-        index.isr_time = now - INDEX_TIMER_PERIOD;
+    if (time_diff(index.trigger_time, now) > INDEX_TIMER_PERIOD)
+        index.trigger_time = now - INDEX_TIMER_PERIOD;
     IRQ_global_enable();
     timer_set(&index.timer, now + INDEX_TIMER_PERIOD);
 }
 
+static void op_delay_timer(void *unused)
+{
+    while (time_diff(time_now(), op_delay.timer.deadline) > 0)
+        cpu_relax();
+    op_delay.mask = 0;
+}
+
 /*
  * Local variables:
  * mode: C