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:
David Given
2018-10-09 00:23:21 +02:00
parent 443082f3c4
commit 40b9fbc25d
7 changed files with 137 additions and 47 deletions

View File

Binary file not shown.

View File

@@ -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)
{

View File

@@ -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:

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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
View File

@@ -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;
}