having good test programs is important. here's the status interface register dropping values. the Teensy program is just writing an incrementing number, and the diagnostics program is checking for gaps.

got that all sorted out. it was a synchronization issue with the flags between the two interfaces.

this is the "seek" command successfully completing! this is a *major* step since it requires 4 working mailboxes and interrupts.

another important step today--i got the data port and data port mailbox flags working. it can also detect 8-bit vs 16-bit transfers. getting very close to working PIO transfers.
nice! I managed to get PIO data transfers working well enough for the buffer test routine to pass.
ok this is fantastic--I've managed to transfer my first actual sector! it's just using PIO and the data is not from a real filesystem, but this is another big step forward!

DMA on Micro Channel is really hard. i'm running a bunch of simulations first, making adjustments to the logic as needed.

so many moving parts.

wow, got four bytes to transfer successfully over DMA! not sure why it got stuck after that.

just ran the same test again and it transferred the whole sector over DMA!!

so at least read transfers are working partially. writes just hang the machine after transferring half a sector. it's probably time for the logic analyzer.

not sure why I always end up in front of a logic analyzer, but here we are.
several issues. this first issue, during a host to device write, holds the arbitration bus too long. it should release immediately after the second arb/gnt pulse
had a theory and it reproduces in simulation. the transfer request flag isn't getting cleared soon enough. la_dma_selected is what can clear this flag and it is changed on the falling edge of cmd, which is too late to catch the ARB/GNT pulse.
yes, that solves the crashing problem. but data isn't getting transferred correctly, so I've got more work to do.
weirdly enough, it works the second try!!! something on the host was prematurely turning off DMA. maybe a bug in difdiag.
so the interrupt_detected flag is supposed to be set in the irq14 handler, and it is *supposed* to be set only when DMA is done. but somehow interrupt_detected is set without the IRQ handler ever being called! then the DMA operation is broken down prematurely.
using the logic analyzer, i proved that the irq14 handler never gets called. the only code that *ever* sets the interrupt_detected flag exists in this handler. it's declared as a volatile so it can't be cached in a register.
I wrote the flag value out to an unused IO port, 0x4F, so I can see it on the logic analyzer. a neat trick!

so i don't know how this flag is getting set. my hack is to preemptively clear the flag right before starting DMA, and so far, it seems to be working.

i think this code was "working" with the real ESDI drive because that one uses burst mode DMA and it finishes up very quickly, before the irq14wait routine can exit early.

decided to look at the real drive. and guess what--it's not using burst mode. the POS registers have it turned off by default. it's also slow to read the data from the spinning disk, so IBM must have figured that it wasn't really necessary.
now I'm reading up on accessing SD cards from the Teensy 4.1. looks like SdFat is the library? could it be so easy?
turns out it's easy but I had to reformat the SD card using the official sdcard.org utility. anyway, I've read the first sector from a real disk image!
the drive now gives the POS ID. let's try to boot!
hmm 01048200 is a drive select acknowledgement error.

the BIOS runs faster than the DIFDIAG utility, and so it seems like it is hitting a timing problem that i didn't hit before.

my drive code seems to randomly hang up and not respond correctly.

it's occasionally getting a spurious end-of-interrupt command which is really odd and points to an issue with the mailboxes (again, sigh).

but it's SO DARN CLOSE. it's transferring sectors from the IML region in the disk image.

figured out one problem. the disk boot routines slam the drive with an ATN and the first command word in 5.5us. the Teensy code takes too long to see the ATN and clears the command register full flag, which drops the first word. oops.
so it *almost* boots now. in fact it successfully loads the IML sectors from the hidden partition on the drive, and no longer throws an I999... error code!

my drive doesn't implement this weird feature called pseudo RBAs--it's a way to artificially limit the maximum possible block address, presumably so they can hide the partition data.

i suspect the BIOS checks this, so i'll have to implement it. ugh. that means i need to figure out this incomprehensible diagram.

holy crap it's booting I can't believe it sdfadfsdfsdfsffasdf
well, it's working well enough to run qbasic. right now the drive is read-only.
i think i need to dig into the 01290200 cache error that has been coming up. i'm concerned that an issue with my DBA-ESDI card has caused it, but i'm not sure.
looks like the cache is inside the CPU. i can't find any cache chips on the motherboard.
see? no cache or memory chips. the larger devices are probably semicustom gate array parts that IBM was fond of using. doubt they contain any cache memory.
looks like the error is generated by an NMI that gets tripped when the cache is being set up. could be a number of causes but in general it is an issue with the internal CPU cache.
could also be this test of the DMA controller which is also included in the same set of tests and triggers the same error code, for some reason.
this gives me an idea.
pulling the CMOS battery...
hmm, the error still comes up. so i just tried what i *should have tried* at the start -- the 700 series diagnostic disk.
when the diagnostic detects the cache error, it asks if you have replaced the CPU card. i *lied to it* and said that I had, so when it asked if i wanted to keep the cache disabled, i said "N".
aaaand that fixed it! we're now booting to DOS off my DBA-ESDI disk replacement.
so here's what i think happened:
1. my early version of the FPGA code had a typo that caused the BURST# line to be held low
2. this caused the DMA controller to get stuck and time out during the cache test, presumably a very early CPU test that checks for cache coherency.
3. this error is *sticky* and gets written to some nonvolatile memory (perhaps not CMOS since i couldn't clear it by pulling the battery.)
this is all very good because i know the root cause and it's not something terrible like data bus contention, and it's thankfully not permanent damage.
it boots windows 3.1 now. it was trying to run a weird hdd power saving mode command I hadn't implemented. it also complains about the swap file because the filesystem is read only still.
so about that write issue: it's an off-by-two error somewhere. two bytes being a single 16-bit word, so it's really an off-by-one error.

figured it out and fixed it. i forget to set the "transfer request" flag to kick off DMA.

in another routine, it sees that this flag is clear and assumes that a word has already been read using DMA, so it reads a crap value and then sets the transfer request flag again to start the next DMA transfer. that "crap value" pushes the valid data forward by one word.

on to the next issues: randomly the ATN register mailbox flag gets set but the data in it is stale. also, the status interface register will randomly get read from by the host.

I think these are two facets of the same problem: the mailbox flags sometimes respond when you access a register that they are not supposed to be monitoring!

the mystery deepens. according to the logic analyzer, temp_atn_set never goes high. reg_atn_set (for crossing clock domains) is always 000. flag_atn is only set to 1 on this single line of code!

and yet, somehow, it magically flips to a 1.

looking at the generated logic, i see no explanation either. temp_atn_set (aka sd_cmd, my test point) never goes high. no glitches, no nothing. to set the flop, EN must be high and R must be low, and a clock edge must occur.
there's a glitch! that's why I missed it before, it's only 2ns. this is the signal from the MCA bus clock domain, and it's getting picked up in my other clock domain's edge detector.
and i believe this is the cause. this line right here. each signal, la_*, is an output from a flip flop latched by the micro channel bus cmd line. however, this line of code creates some combinational logic--there's a timing hazard here...

the problem? the line (la_addr == REG_ATN) creates a bunch of gates that are slightly slower than the simple AND gates in the previous part of the line.

so la_mca_op=1, ~la_s0_w_l=1, and (la_addr == REG_ATN) *is also a 1 for a very short time!!!* this is because the previous value of la_addr WAS a REG_ATN.

what i need to do is take that entire wire and turn it into a latch (a reg) and clock it on cmd.

so here's the solution: all the signals in the MCA bus domain go to a latch clocked in that domain (the first "always" block).

then *without any combinational logic* the output of that latch goes *directly* to another latch (the second "always" block) located in the main clock domain.

(i have another flip flop in main clock domain just for detecting the edge)

next step is to optimize the interface speed. right now it takes 25us to read a sector from the SD card but ~5 milliseconds (ouch) to DMA it to the PC!

it's mostly an issue with the Teensy-to-FPGA interface, which is async and simple: 4 address lines, 16 data lines, a read control line, and a write control line. everything else is done as a register in the 4-bit address space. flag register for status and mailbox sync bits.

why is it 5ms per sector for DMA? well, it's about 20us per word. most of that time is wasted by the slow interface between the Teensy and the FPGA. I really should fix that.
got rid of some delays and now we're down to 6us per word. but there are some unexpected wide gaps in between transfers between the FPGA and the Teensy.
ahh that must be the issue, when i change from a read to a write, i have to set the port direction for 16 IO pins. pinMode is uhh not quick, so let's try writing to the port direction register directly.
that's better, it's 2us per word now.
so how does this compare with the real drive? it transfers a word in about 1.6us (compared to 2us for mine), so it is slightly faster for sustained data transfer with data that fits in the internal buffer. however, seek times (in the ms range) more or less cancel this out.
here's a DMA cycle for the real hard drive. it's using the bus more efficiently, requesting a new transfer as soon as the transfer completes. my design waits for the full DMA cycle to end before requesting a new one.

maybe theoretical maximum transfer rates would make the comparison easier:
My drive: ~1MB/s
stock HDD: ~1.25MB/s (neglecting seek)

if i put in a bunch of work and implemented burst mode DMA transfer, i might be able to hit 4.5MB/s.

no more debugging wires. the DBA-ESDI drive is now running stand-alone!
I printed an angled carrier. the angle is because the socketed Teensy is really tall.
@tubetime Damn, that was impressive.

@tubetime

Their IDE drives from around the time period were around 600kbytes/sec

@bitsavers i guess that means i'm nearly done 😅

@tubetime

it would be interesting to see a block diagram of what the microchannel interface looks like to the teensy

@tubetime
metastability farther back in the clock domain crossing?
@tubetime of course it is. 🙂 Thinking it was an off by two error was off by one. 😂
@tubetime This was really enjoyable to follow along with! Congrats on the progress
@tubetime simulating an ESDI disk? Now I need to read up on it. I remember when ESDI vs SCSI was a serious question
@Tedspence not standard ESDI--this is DBA-ESDI.
@tubetime @Tedspence is this “IBM” ESDI? I haven’t a bunch of systems that use those big ass IBM 5.25” ESDI disks and those disks currently have like a 90% failure rate. This could rescue many machines! Please sign me up for your newsletter!
@jgeorge only if those are DBA-ESDI, not regular ESDI.