Verilog SPI module functioning in unpredicted ways
As per our discussion in the chat, the issue appears to be down to the built in simulator. When I first started using it, I ran into some weird issues as well, so quickly moved over to using ModelSim (which comes with Quartus).

I think the issue is basically down to how you enter the waveforms in the built-in simulator - graphically. This can confuse the simulator, especially if you have mixtures of non-blocking/blocking assignments, and are using lots of clock edges - basically the cause-effect appears to get a bit mixed up.

This becomes clear when using ModelSim to simulate the same design. We get the following waveform:



Which is very different in many places.

The results here are from the module in your post being controlled by a Verilog testbench rather than a graphical waveform editor. This not only makes editing the simulation waveforms easier, but also ensures cause-effect of signals is maintained as you can guarantee everything is done correctly at clock edges.



As you are coming from Xilinx/Vivado and testbenches are familiar, I won't go into much detail on them. What I will instead do is point out a couple of useful links:


Getting Started with Quartus II Simulation Using the ModelSim-Altera Software
Simulation With the NativeLink Feature in Quartus II Software


These basically are breadcrumbs in setting up ModelSim to be run directly from Quartus. This will allow you to ditch the graphical simulator and use ModelSim. I say breadcrumbs because the information is very scattered/incomplete. I have been successful at using testbenches directly from Quartus with some minor annoyance. I actually find it much easier to simulate directly with ModelSim using simple commands for compiling and simulating.

An example of how to load a quick simulation (which is of the load_sim.tcl script I attached in our discussion):

# Location of simulation files
set SIMDIR "../"

# Create a work library - lets call it "spi", but you could call it anything
vlib spi

# Map the library to "work"
vmap work ./spi

# Compile the the simulation files:
# vlog filename.v tells modelsim to compile a Verilog file. Add an entry for each file
vlog $SIMDIR/spi.v

# Compile the testbench
# I wrote a quick SystemVerilog called test_program.sv, so lets compile that using the -sv flag (to indicate the language is SV).
vlog -sv ./test_program.sv

# Elaborate top level design
# test_program is the name of the testbench module
eval vsim -t ns -L work -L spi test_program

# Load the waveform "do file" Tcl script
# This is an optional file which stores things like which waveforms you want to display
# and what radix they use. You can generate this after the first run by clicking in the
# wave view and pressing CTRL-S. This will save a .do file with the setup

do ./wave.do

# Run the simulation for 1000 ns. You could do "run -all" if you have a $stop command in
# your testbench.
run 1000ns


With this script it is quite easy to set up a simulation for ModelSim and use a testbench in a similar way to what you would do in Vivado. You simply change to the correct directory in ModelSim, and then enter at the prompt do load_sim.tcl and the simulation will run.