Note: I have edited this question from its original content since I was able to find a deeper cause/symptom for the problem. I have re-written it to focus on that instead.
I am using a pretty basic configuration for the STM32F405 using the CubeMX configuration system.
Something (a clock, a setting.. something) is not configured correctly, and I can't tell how I could have possibly caused it. Though it could be a problem with the PCB I designed, that seems unlikely as the code runs and debugs and the SysTick interrupt is advancing the timing counter just fine.
I have tracked part of the problem down to this function
/* Reset after a PHY select and set Host mode */
Which times out, presumably because the core never comes out of reset (OTG_FS_GRSTCTL:CSRST == 1 always, after being set in that function. USB_CoreReset() tries to read CSRST==0 200,000 times, and if it fails it returns unsuccessfully).
If you would like to generate this file and look at the output (or adapt it to your board), you can take this text and paste it into a file called "USB CSRST Problem.ioc". Note: It will probably require some tweaking for your board since I have some pins assigned to outputs for an LCD display.
I have tracked a discussion down that describes similar symptoms, but I can confirm that my code does set OTG_HS_GUSBCFG:PHYSEL properly (confirmed setting bit 6 before the reset is performed), and generally adheres to the recommended startup procedure as outlined in that thread.