#include #include #include #include #include #include "project.h" #include "../protocol.h" #include "../lib/common/crunch.h" #define MOTOR_ON_TIME 5000 /* milliseconds */ #define STEP_INTERVAL_TIME 6 /* ms */ #define STEP_SETTLING_TIME 50 /* ms */ #define DISKSTATUS_WPT 1 #define DISKSTATUS_DSKCHG 2 #define STEP_TOWARDS0 1 #define STEP_AWAYFROM0 0 static volatile uint32_t clock = 0; static volatile bool index_irq = false; static bool motor_on = false; static uint32_t motor_on_time = 0; static bool homed = false; static int current_track = 0; static uint8_t current_drive_flags = 0; #define BUFFER_COUNT 16 #define BUFFER_SIZE 64 static uint8_t td[BUFFER_COUNT]; static uint8_t dma_buffer[BUFFER_COUNT][BUFFER_SIZE] __attribute__((aligned())); static uint8_t usb_buffer[BUFFER_SIZE] __attribute__((aligned())); static uint8_t dma_channel; #define NEXT_BUFFER(b) (((b)+1) % BUFFER_COUNT) static volatile int dma_writing_to_td = 0; static volatile int dma_reading_from_td = 0; static volatile bool dma_underrun = false; #define DECLARE_REPLY_FRAME(STRUCT, TYPE) \ STRUCT r = {.f = { .type = TYPE, .size = sizeof(STRUCT) }} static void system_timer_cb(void) { CyGlobalIntDisable; clock++; CyGlobalIntEnable; } CY_ISR(index_irq_cb) { index_irq = true; /* Stop writing the instant the index pulse comes along; it may take a few * moments for the main code to notice the pulse, and we don't want to overwrite * the beginning of the track. */ ERASE_REG_Write(0); } CY_ISR(capture_dma_finished_irq_cb) { dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td); if (dma_writing_to_td == dma_reading_from_td) dma_underrun = true; } CY_ISR(replay_dma_finished_irq_cb) { dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td); if (dma_reading_from_td == dma_writing_to_td) dma_underrun = true; } static void print(const char* msg, ...) { char buffer[64]; va_list ap; va_start(ap, msg); vsnprintf(buffer, sizeof(buffer), msg, ap); va_end(ap); UART_PutString(buffer); UART_PutCRLF(); } static void start_motor(void) { if (!motor_on) { MOTOR_REG_Write(1); CyDelay(1000); homed = false; } motor_on_time = clock; motor_on = true; CyWdtClear(); } static void wait_until_writeable(int ep) { while (USBFS_GetEPState(ep) != USBFS_IN_BUFFER_EMPTY) ; } static void send_reply(struct any_frame* f) { print("reply 0x%02x", f->f.type); wait_until_writeable(FLUXENGINE_CMD_IN_EP_NUM); USBFS_LoadInEP(FLUXENGINE_CMD_IN_EP_NUM, (uint8_t*) f, f->f.size); } static void send_error(int code) { DECLARE_REPLY_FRAME(struct error_frame, F_FRAME_ERROR); r.error = code; send_reply((struct any_frame*) &r); } /* buffer must be big enough for a frame */ static int usb_read(int ep, uint8_t buffer[FRAME_SIZE]) { int length = USBFS_GetEPCount(ep); USBFS_ReadOutEP(ep, buffer, length); while (USBFS_GetEPState(ep) == USBFS_OUT_BUFFER_FULL) ; return length; } static void cmd_get_version(struct any_frame* f) { DECLARE_REPLY_FRAME(struct version_frame, F_FRAME_GET_VERSION_REPLY); r.version = FLUXENGINE_VERSION; send_reply((struct any_frame*) &r); } static void step(int dir) { STEP_REG_Write(dir); CyDelayUs(1); STEP_REG_Write(dir | 2); CyDelayUs(1); STEP_REG_Write(dir); CyDelay(STEP_INTERVAL_TIME); } static void seek_to(int track) { start_motor(); if (!homed) { print("homing"); while (!TRACK0_REG_Read()) step(STEP_TOWARDS0); /* Step to -1, which should be a nop, to reset the disk on disk change. */ step(STEP_TOWARDS0); homed = true; current_track = 0; CyDelayUs(1); /* for direction change */ } print("beginning seek from %d to %d", current_track, track); while (track != current_track) { if (TRACK0_REG_Read()) { if (current_track != 0) print("unexpectedly detected track 0"); current_track = 0; } if (track > current_track) { step(STEP_AWAYFROM0); current_track++; } else if (track < current_track) { step(STEP_TOWARDS0); current_track--; } CyWdtClear(); } CyDelay(STEP_SETTLING_TIME); print("finished seek"); } static void cmd_seek(struct seek_frame* f) { seek_to(f->track); DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_SEEK_REPLY); send_reply(&r); } static void cmd_recalibrate(void) { homed = false; seek_to(0); DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_RECALIBRATE_REPLY); send_reply(&r); } static void cmd_measure_speed(struct any_frame* f) { start_motor(); index_irq = false; while (!index_irq) ; index_irq = false; int start_clock = clock; while (!index_irq) ; int end_clock = clock; DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY); r.period_ms = end_clock - start_clock; send_reply((struct any_frame*) &r); } static void cmd_bulk_test(struct any_frame* f) { uint8_t buffer[64]; wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); for (int x=0; x<64; x++) for (int y=0; y<256; y++) { for (unsigned z=0; zside); seek_to(current_track); /* Do slow setup *before* we go into the real-time bit. */ SAMPLER_CONTROL_Write(1); /* reset */ { uint8_t i = CyEnterCriticalSection(); SAMPLER_DATAPATH_F0_SET_LEVEL_MID; SAMPLER_DATAPATH_F0_CLEAR; SAMPLER_DATAPATH_F0_SINGLE_BUFFER_UNSET; CyExitCriticalSection(i); } wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); init_capture_dma(); /* Wait for the beginning of a rotation. */ print("wait"); index_irq = false; while (!index_irq) ; index_irq = false; crunch_state_t cs = {}; cs.outputptr = usb_buffer; cs.outputlen = BUFFER_SIZE; dma_writing_to_td = 0; dma_reading_from_td = -1; dma_underrun = false; int count = 0; SAMPLER_CONTROL_Write(0); /* !reset */ CAPTURE_CONTROL_Write(1); CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]); CyDmaClearPendingDrq(dma_channel); CyDmaChEnable(dma_channel, 1); /* Wait for the first DMA transfer to complete, after which we can start the * USB transfer. */ while ((dma_writing_to_td == 0) && !index_irq) ; dma_reading_from_td = 0; /* Start transferring. */ int revolutions = f->revolutions; while (!dma_underrun) { CyWdtClear(); /* Have we reached the index pulse? */ if (index_irq) { index_irq = false; revolutions--; if (revolutions == 0) break; } /* Wait for the next block to be read. */ while (dma_reading_from_td == dma_writing_to_td) { /* On an underrun, give up immediately. */ if (dma_underrun) goto abort; } uint8_t dma_buffer_usage = 0; while (dma_buffer_usage < BUFFER_SIZE) { cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage; cs.inputlen = BUFFER_SIZE - dma_buffer_usage; crunch(&cs); dma_buffer_usage += BUFFER_SIZE - cs.inputlen; count++; if (cs.outputlen == 0) { while (USBFS_GetEPState(FLUXENGINE_DATA_IN_EP_NUM) != USBFS_IN_BUFFER_EMPTY) { if (index_irq || dma_underrun) goto abort; } USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE); cs.outputptr = usb_buffer; cs.outputlen = BUFFER_SIZE; } } dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td); } abort:; CAPTURE_CONTROL_Write(0); CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN); while (CyDmaChGetRequest(dma_channel)) ; donecrunch(&cs); wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); unsigned zz = cs.outputlen; if (cs.outputlen != BUFFER_SIZE) USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen); if ((cs.outputlen == BUFFER_SIZE) || (cs.outputlen == 0)) USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0); wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM); deinit_dma(); if (dma_underrun) { print("underrun after %d packets"); send_error(F_ERROR_UNDERRUN); } else { DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_READ_REPLY); send_reply(&r); } print("count=%d i=%d d=%d zz=%d", count, index_irq, dma_underrun, zz); } static void init_replay_dma(void) { dma_channel = SEQUENCER_DMA_DmaInitialize( 1 /* bytes */, true /* request per burst */, HI16(CYDEV_SRAM_BASE), HI16(CYDEV_PERIPH_BASE)); for (int i=0; ibytes_to_write % FRAME_SIZE) { send_error(F_ERROR_INVALID_VALUE); return; } SIDE_REG_Write(f->side); SEQUENCER_CONTROL_Write(1); /* reset */ { uint8_t i = CyEnterCriticalSection(); SEQUENCER_DATAPATH_F0_SET_LEVEL_NORMAL; SEQUENCER_DATAPATH_F0_CLEAR; SEQUENCER_DATAPATH_F0_SINGLE_BUFFER_UNSET; CyExitCriticalSection(i); } seek_to(current_track); init_replay_dma(); bool writing = false; /* to the disk */ bool finished = false; int packets = f->bytes_to_write / FRAME_SIZE; int count_written = 0; int count_read = 0; dma_writing_to_td = 0; dma_reading_from_td = -1; dma_underrun = false; crunch_state_t cs = {}; cs.outputlen = BUFFER_SIZE; USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM); int old_reading_from_td = -1; for (;;) { /* Read data from USB into the buffers. */ if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td) { if (writing && (dma_underrun || index_irq)) goto abort; /* Read crunched data, if necessary. */ if (cs.inputlen == 0) { if (finished) { /* There's no more data to read, so fake some. */ for (int i=0; iside); seek_to(current_track); /* Disk is now spinning. */ print("start erasing"); index_irq = false; while (!index_irq) ; ERASE_REG_Write(1); index_irq = false; while (!index_irq) ; ERASE_REG_Write(0); print("stop erasing"); DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_ERASE_REPLY); send_reply((struct any_frame*) &r); } static void cmd_set_drive(struct set_drive_frame* f) { if (current_drive_flags != f->drive_flags) { current_drive_flags = f->drive_flags; DRIVE_REG_Write(current_drive_flags); homed = false; } DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_SET_DRIVE_REPLY); send_reply((struct any_frame*) &r); } static void handle_command(void) { static uint8_t input_buffer[FRAME_SIZE]; (void) usb_read(FLUXENGINE_CMD_OUT_EP_NUM, input_buffer); struct any_frame* f = (struct any_frame*) input_buffer; print("command 0x%02x", f->f.type); switch (f->f.type) { case F_FRAME_GET_VERSION_CMD: cmd_get_version(f); break; case F_FRAME_SEEK_CMD: cmd_seek((struct seek_frame*) f); break; case F_FRAME_MEASURE_SPEED_CMD: cmd_measure_speed(f); break; case F_FRAME_BULK_TEST_CMD: cmd_bulk_test(f); break; case F_FRAME_READ_CMD: cmd_read((struct read_frame*) f); break; case F_FRAME_WRITE_CMD: cmd_write((struct write_frame*) f); break; case F_FRAME_ERASE_CMD: cmd_erase((struct erase_frame*) f); break; case F_FRAME_RECALIBRATE_CMD: cmd_recalibrate(); break; case F_FRAME_SET_DRIVE_CMD: cmd_set_drive((struct set_drive_frame*) f); break; default: send_error(F_ERROR_BAD_COMMAND); } } int main(void) { CyGlobalIntEnable; CySysTickStart(); CySysTickSetCallback(4, system_timer_cb); INDEX_IRQ_StartEx(&index_irq_cb); CAPTURE_DMA_FINISHED_IRQ_StartEx(&capture_dma_finished_irq_cb); SEQUENCER_DMA_FINISHED_IRQ_StartEx(&replay_dma_finished_irq_cb); DRIVE_REG_Write(0); UART_Start(); USBFS_Start(0, USBFS_DWR_VDDD_OPERATION); CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED); /* UART_PutString("GO\r"); */ for (;;) { CyWdtClear(); if (motor_on) { uint32_t time_on = clock - motor_on_time; if (time_on > MOTOR_ON_TIME) { MOTOR_REG_Write(0); motor_on = false; } } if (!USBFS_GetConfiguration() || USBFS_IsConfigurationChanged()) { print("Waiting for USB..."); while (!USBFS_GetConfiguration()) ; print("USB ready"); USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM); } if (USBFS_GetEPState(FLUXENGINE_CMD_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL) { handle_command(); USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM); print("idle"); } } }