Aha!
The problem is that the MAC is inserting tx_bus.data into the outbound data stream at the end of the padding in the last cycle when it starts calculating the CRC, rather than 0x00000000 like it should.
All of my previous code using the same MAC probably sent 0 on tx_bus.data during the inter-frame gap, rather than holding the last word of the frame, so the bug never got triggered.