Turns out that while I did have a small math error (two *pulses* per revolution on the green wire, not two *toggles* per revolution), the main error was actually in my bit-serial divider IP.
Which I had written back in grad school for my thesis, and it worked great on that CPU because I happened to have the inputs stable from when a divide was issued until it retired. The interface spec called for the divider to register the inputs on the first cycle, but one line of code used the unregistered value instead. Oops!
Anyway, I now have working fan tachometers (no PWM outputs yet, so they're always at max RPM), plus I can read the FPGA sensors using the XADC, and the I2C sensors scattered around the board.
The STM32 also has an on-die temp sensor which I'm not using yet, but I think that's the only missing bit.