mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Fix ghastly DMA ordering bug leading to corrupted writes. Writes are less
corrupted! Still can't reliably read back what I wrote, though. Added a test pattern feature to write, to aid debugging.
This commit is contained in:
Binary file not shown.
@@ -47,6 +47,11 @@ static void system_timer_cb(void)
|
||||
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)
|
||||
@@ -352,6 +357,12 @@ static void init_replay_dma(void)
|
||||
|
||||
static void cmd_write(struct write_frame* f)
|
||||
{
|
||||
if (f->bytes_to_write % FRAME_SIZE)
|
||||
{
|
||||
send_error(F_ERROR_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
SIDE_REG_Write(f->side);
|
||||
seek_to(current_track);
|
||||
|
||||
@@ -359,15 +370,14 @@ static void cmd_write(struct write_frame* f)
|
||||
bool writing = false; /* to the disk */
|
||||
bool listening = false;
|
||||
bool finished = false;
|
||||
int packets = (f->bytes_to_write+FRAME_SIZE-1) / FRAME_SIZE;
|
||||
int packets = f->bytes_to_write / FRAME_SIZE;
|
||||
int count_read = 0;
|
||||
int count_written = 0;
|
||||
REPLAY_COUNTER_Start();
|
||||
REPLAY_COUNTER_WriteCounter(0);
|
||||
dma_writing_to_td = 0;
|
||||
dma_reading_from_td = -1;
|
||||
dma_underrun = false;
|
||||
CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]);
|
||||
CyDmaClearPendingDrq(dma_channel);
|
||||
|
||||
int old_reading_from_td = -1;
|
||||
for (;;)
|
||||
@@ -393,53 +403,68 @@ static void cmd_write(struct write_frame* f)
|
||||
|
||||
writing = true;
|
||||
ERASE_REG_Write(1); /* start erasing! */
|
||||
REPLAY_DMA_FINISHED_IRQ_Enable();
|
||||
dma_underrun = false;
|
||||
CyDmaChSetInitialTd(dma_channel, td[dma_reading_from_td]);
|
||||
CyDmaClearPendingDrq(dma_channel);
|
||||
CyDmaChEnable(dma_channel, 1); /* nothing will happen... */
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_REQ); /* ...until we manually start the first transfer */
|
||||
}
|
||||
|
||||
/* ...unless we reach the end of the track, of course. */
|
||||
/* ...unless we reach the end of the track or suffer underrung, of course. */
|
||||
|
||||
if (index_irq)
|
||||
if (index_irq || dma_underrun)
|
||||
break;
|
||||
}
|
||||
|
||||
if (NEXT_BUFFER(dma_writing_to_td) != dma_reading_from_td)
|
||||
{
|
||||
/* We're ready for more data from USB. */
|
||||
/* We're ready for more data. */
|
||||
|
||||
if (!listening)
|
||||
if (finished)
|
||||
{
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
listening = true;
|
||||
}
|
||||
|
||||
/* Is more data actually ready? */
|
||||
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, dma_buffer[dma_writing_to_td]);
|
||||
if ((length < FRAME_SIZE) || (packets == 1))
|
||||
{
|
||||
print("early end of data\r");
|
||||
finished = true;
|
||||
}
|
||||
listening = false;
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
/* The USB stream has stopped early, so just fake data to keep the writer happy. */
|
||||
|
||||
count_read++;
|
||||
memset(dma_buffer[dma_writing_to_td], 0x30, BUFFER_SIZE);
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make sure we're waiting for USB data. */
|
||||
|
||||
if (!listening)
|
||||
{
|
||||
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
|
||||
listening = true;
|
||||
}
|
||||
|
||||
/* Is more data actually ready? */
|
||||
|
||||
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
|
||||
{
|
||||
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, dma_buffer[dma_writing_to_td]);
|
||||
listening = false;
|
||||
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
|
||||
|
||||
count_read++;
|
||||
if ((length < FRAME_SIZE) || (count_read == packets))
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once we have enough data, we're ready to start writing. */
|
||||
/* Only start writing once the buffer is full. */
|
||||
|
||||
if (dma_reading_from_td == -1)
|
||||
if ((dma_reading_from_td == -1) && (dma_writing_to_td == BUFFER_COUNT-1))
|
||||
dma_reading_from_td = old_reading_from_td = 0;
|
||||
}
|
||||
}
|
||||
|
||||
REPLAY_DMA_FINISHED_IRQ_Disable();
|
||||
|
||||
if (writing)
|
||||
{
|
||||
print("stop writing\r");
|
||||
WGATE_Write(0);
|
||||
ERASE_REG_Write(0);
|
||||
print("stop writing after ");
|
||||
printi(count_written);
|
||||
print("\r");
|
||||
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
|
||||
while (CyDmaChGetRequest(dma_channel))
|
||||
;
|
||||
@@ -451,10 +476,12 @@ static void cmd_write(struct write_frame* f)
|
||||
{
|
||||
print("underrun after ");
|
||||
printi(count_read);
|
||||
print(" out of ");
|
||||
printi(packets);
|
||||
print(" packets read\r");
|
||||
send_error(F_ERROR_UNDERRUN);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_REPLY_FRAME(struct write_reply_frame, F_FRAME_WRITE_REPLY);
|
||||
r.bytes_actually_written = count_written*FRAME_SIZE;
|
||||
|
||||
@@ -477,6 +504,13 @@ static void cmd_write(struct write_frame* f)
|
||||
}
|
||||
|
||||
deinit_dma();
|
||||
|
||||
if (dma_underrun)
|
||||
{
|
||||
send_error(F_ERROR_UNDERRUN);
|
||||
return;
|
||||
}
|
||||
|
||||
send_reply((struct any_frame*) &r);
|
||||
}
|
||||
|
||||
@@ -535,7 +569,6 @@ int main(void)
|
||||
for (;;)
|
||||
{
|
||||
CyWdtClear();
|
||||
ERASE_REG_Write(0); /* belt and braces. */
|
||||
|
||||
if (motor_on)
|
||||
{
|
||||
|
||||
13
README.md
13
README.md
@@ -25,8 +25,17 @@ soldering iron.
|
||||
Some useful numbers:
|
||||
|
||||
- nominal rotation speed is 300 rpm, or 5Hz. The period is 200ms.
|
||||
- MFM encoding uses a clock of 500kHz. This makes each recording cell 2us.
|
||||
- a pulse is 150ns to 800ns, so a clock of 7MHz will sample it.
|
||||
- a pulse is 150ns to 800ns long.
|
||||
- a 12MHz tick is 83ns.
|
||||
- MFM HD encoding uses a clock of 500kHz. This makes each recording cell 2us,
|
||||
or 24 ticks. For DD it's 4us and 48 ticks.
|
||||
- a short transition is one cell (2us == 24 ticks). A medium is a cell and
|
||||
a half (3us == 36 ticks). A long is two cells (4us == 48 ticks). Double
|
||||
that for DD.
|
||||
- pulses are detected with +/- 350ns error for HD and 700ns for DD. That's
|
||||
4 ticks and 8 ticks. That seems to be about what we're seeing.
|
||||
- in real life, start going astray after about 128 ticks == 10us. If you
|
||||
don't write anything, you read back random noise.
|
||||
|
||||
Useful links:
|
||||
|
||||
|
||||
66
cmd_write.c
66
cmd_write.c
@@ -3,6 +3,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
static const char* filename = NULL;
|
||||
static bool test_pattern = false;
|
||||
static int start_track = 0;
|
||||
static int end_track = 79;
|
||||
static int start_side = 0;
|
||||
@@ -13,7 +14,8 @@ static void syntax_error(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"syntax: fluxclient write <options>:\n"
|
||||
" -o <filename> input filename\n"
|
||||
" -o <filename> input filename (can't use with -T)\n"
|
||||
" -T write a test pattern (can't use with -o)\n"
|
||||
" -s <start track> defaults to 0\n"
|
||||
" -e <end track> defaults to 79\n"
|
||||
" -0 read just side 0 (defaults to both)\n"
|
||||
@@ -26,7 +28,7 @@ static char* const* parse_options(char* const* argv)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
switch (getopt(countargs(argv), argv, "+o:s:e:01"))
|
||||
switch (getopt(countargs(argv), argv, "+o:Ts:e:01"))
|
||||
{
|
||||
case -1:
|
||||
return argv + optind - 1;
|
||||
@@ -35,6 +37,10 @@ static char* const* parse_options(char* const* argv)
|
||||
filename = optarg;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
test_pattern = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
start_track = atoi(optarg);
|
||||
break;
|
||||
@@ -68,17 +74,42 @@ static void open_file(void)
|
||||
atexit(close_file);
|
||||
}
|
||||
|
||||
static void write_data(struct raw_data_buffer* buffer, int ticks, int* cursor, uint8_t data)
|
||||
{
|
||||
while ((ticks > 0) && (*cursor < buffer->len))
|
||||
{
|
||||
buffer->buffer[(*cursor)++] = data;
|
||||
ticks -= data;
|
||||
}
|
||||
}
|
||||
|
||||
static void create_test_pattern(struct raw_data_buffer* buffer)
|
||||
{
|
||||
int cursor = 0;
|
||||
const int five_milliseconds = 5 * TICK_FREQUENCY / 1000;
|
||||
int step = 0x38;
|
||||
|
||||
while (cursor < buffer->len)
|
||||
{
|
||||
write_data(buffer, five_milliseconds, &cursor, step);
|
||||
write_data(buffer, five_milliseconds, &cursor, 0x30);
|
||||
if (step < 0xf8)
|
||||
step += 8;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_write(char* const* argv)
|
||||
{
|
||||
argv = parse_options(argv);
|
||||
if (countargs(argv) != 1)
|
||||
syntax_error();
|
||||
if (!filename)
|
||||
error("you must supply a filename to read from");
|
||||
if (!!filename == test_pattern)
|
||||
error("you must specify a filename to read from, or a test pattern");
|
||||
if (start_track > end_track)
|
||||
error("writing to track %d to track %d makes no sense", start_track, end_track);
|
||||
|
||||
open_file();
|
||||
if (filename)
|
||||
open_file();
|
||||
|
||||
for (int t=start_track; t<=end_track; t++)
|
||||
{
|
||||
@@ -90,13 +121,28 @@ void cmd_write(char* const* argv)
|
||||
|
||||
struct raw_data_buffer buffer;
|
||||
buffer.len = sizeof(buffer.buffer);
|
||||
if (!sql_read_flux(db, t, side, buffer.buffer, &buffer.len))
|
||||
printf("no data in file\n");
|
||||
else
|
||||
if (filename)
|
||||
{
|
||||
printf("sending %ld bytes\n", buffer.len);
|
||||
usb_write(side, &buffer);
|
||||
if (!sql_read_flux(db, t, side, buffer.buffer, &buffer.len))
|
||||
continue;
|
||||
}
|
||||
if (test_pattern)
|
||||
create_test_pattern(&buffer);
|
||||
|
||||
buffer.len &= ~(FRAME_SIZE-1);
|
||||
|
||||
uint32_t time = 0;
|
||||
for (int i=0; i<buffer.len; i++)
|
||||
time += buffer.buffer[i];
|
||||
printf("sent %dms", (time*1000)/TICK_FREQUENCY);
|
||||
fflush(stdout);
|
||||
|
||||
int bytes_actually_written = usb_write(side, &buffer);
|
||||
|
||||
time = 0;
|
||||
for (int i=0; i<bytes_actually_written; i++)
|
||||
time += buffer.buffer[i];
|
||||
printf(", wrote %dms\n", (time*1000)/TICK_FREQUENCY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ extern void usb_seek(int track);
|
||||
extern int usb_measure_speed(void);
|
||||
extern void usb_bulk_test(void);
|
||||
extern void usb_read(int side, struct raw_data_buffer* buffer);
|
||||
extern void usb_write(int side, struct raw_data_buffer* buffer);
|
||||
extern int usb_write(int side, struct raw_data_buffer* buffer);
|
||||
|
||||
extern void cmd_rpm(char* const* argv);
|
||||
extern void cmd_usbbench(char* const* argv);
|
||||
|
||||
@@ -51,6 +51,7 @@ enum
|
||||
F_ERROR_NONE = 0,
|
||||
F_ERROR_BAD_COMMAND,
|
||||
F_ERROR_UNDERRUN,
|
||||
F_ERROR_INVALID_VALUE,
|
||||
};
|
||||
|
||||
struct frame_header
|
||||
|
||||
5
usb.c
5
usb.c
@@ -149,7 +149,8 @@ void usb_read(int side, struct raw_data_buffer* buffer)
|
||||
await_reply(F_FRAME_READ_REPLY);
|
||||
}
|
||||
|
||||
void usb_write(int side, struct raw_data_buffer* buffer)
|
||||
/* Returns number of bytes actually written */
|
||||
int usb_write(int side, struct raw_data_buffer* buffer)
|
||||
{
|
||||
struct write_frame f = {
|
||||
.f = { .type = F_FRAME_WRITE_CMD, .size = sizeof(f) },
|
||||
@@ -161,5 +162,5 @@ void usb_write(int side, struct raw_data_buffer* buffer)
|
||||
large_bulk_transfer(FLUXENGINE_DATA_OUT_EP, &buffer->buffer, buffer->len);
|
||||
|
||||
struct write_reply_frame* r = await_reply(F_FRAME_WRITE_REPLY);
|
||||
printf("written %d bytes\n", r->bytes_actually_written);
|
||||
return r->bytes_actually_written;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user