After much gnashing of teeth and pulling of hair, I finally figured out what was causing load mode to fail on the real hardware despite working on the simulator. My “next state” logic didn’t have a default value, so both the simulator and the synthesizer were inferring a latch. However, somehow the synthesizer wasn’t doing it properly. Adding a “next_state <= state;” statement in the combinatorial process used for the state machine solved the problem.
Now I can toggle in programs and execute them, without needing any initialization of the RAM.
I need to write a slightly fancier demo program, shoot and upload some video, and I can declare victory!