Revisiting Wrap030 Disk Access
I have more ideas for projects than time or budget to work on them. Already this year I've gone completely through the design process for two new large homebrew projects that are currently too large for my project budget, plus a few small ones I never got around to ordering. So rather than spend more than I should taking on a new project, I decided to revisit an existing one.
It's been over a year since I last worked on the original Wrap030 project — my old stack-of-boards MC68030 system. Its current configuration includes the main board with CPU, ROM, RAM, UART, & glue logic; a hand-wired breakout board to add a second UART; a custom video output board; and a mezzanine board with FPU and provision for an IDE disk that is not yet working. It has been functional in this configuration since last February.
My goal for this project from the beginning was to build something capable of running a proper operating system, like Unix System V or Linux. To do that though, I'm going to need to get disk access working.
I had started on disk access, but didn't quite have it functional when I turned my focus to integrating all of boards into the single Wrap030-ATX motherboard. I had added IDE cycles to the CPLD on the mezzanine board, and had added a few rough drafts of disk functions to my ROM. I set the project aside when I realized my function for checking dish presence was reporting a disk was present when there wasn't one.
I have worked with IDE before — my original 68000 project had an IDE port on it. I had gotten that project to the point where I could read a sector of data from the disk, but never could wrap my head around how to actually navigate even a simple file system like FAT16. It was this code that I had adapted for Wrap030, so when it didn't work, I assumed it was a problem with my logic.
Turns out I had just inadvertently clobbered a register in the disk check function. The logic worked just fine. I was able to write a couple quick BASIC programs to read a sector of data and even run code from the boot sector.
My assembly function for reading data from disk however was still not working.
I tried rewriting it in C instead of assembly.
I tried again, and again, and again. I added delays and loops and print statements and everything I could think of. I scoured datasheets, read though all the different release versions of the ATA specification, ported code from other projects, looked at every example of reading from an IDE disk I could find.
No matter what I did, I always got the same result.
This did not make any sense. Reading from an IDE disk involves setting up the sector address, the number of sectors to transfer, sending a read command, and then reading the IDE data port 256 times per sector. Each time the data port is read, the disk will give another 16-bit word of data. But for some reason, all I was getting was the first word of data returned 256 times.
There is nothing in the specification to explain this.
I knew there was nothing wrong with my logic, because I could read the data just fine with my BASIC program or by manually poking the right addresses using the monitor. Maybe there was some edge case affecting timing when running in assembly, but even adding delay loops and print statements didn't have any effect.
I reached out for help. I got great feedback on my read functions and my timing and how IDE and CompactFlash cards worked, but still could not solve this problem.
I had shared my code and was explaining that I had added some extra NOP instructions to enforce minimum time between IDE access cycles in PIO-0 mode. At 25MHz with cache enabled, the 68030 can complete an instruction in as little as 80ns, so a few NOPs would ensure enough time elapsed between cycles.
… cache.
The 68030 has 256 bytes of data cache. My disk read function is running in a tight loop that only really hits a few addresses; not nearly enough to invalidate and flush the entire 256 bytes of cache. The CPU does have a cache inhibit signal to use with peripherals that return new data on subsequent access to the same address, but it turns out I was only asserting it when accessing the UART on the main board.
It's a simple enough hypothesis to test. When I initially added support in my ROM for enabling cache at startup, I included user functions for enabling and disabling cache.
… It was cache all along.
Now I need to add some way to inhibit cache while accessing the IDE port, and then I can move on to trying to use the disk for loading programs.