What is wrong with this attempt at an SDR RAM in Verilog?
I got this working, with one specific change although to be honest some other edits may have helped too.

I thought about how drive_data is set up using combinatorial logic, but the outdata register is clocked. Confirmed with behavioural sim, this means that stale data is driven out during the first part of a read cycle, before the address is latched.

I "fixed" this by changing the always block that sets outdata to do so on every clock, meaning the right data gets in there during the setup phase while address is valid on the bus, before the OE strobe comes along.

I also refactored my memory bus handling into a submodule (inverting the control signals).

module emif(
    input clk,
    input [1:0] addr,
    inout [15:0] data,
    input ce,           // note these signals are active high
    input we,           // (opposite to the PCB signals)
    input oe
    );

wire drive_data;
reg [15:0] mem [0:3];
reg [15:0] em_outdata;

assign drive_data = ce &amp;&amp; oe &amp;&amp; !we;
assign data = drive_data ? em_outdata : 16'bz;

// writes data to small mem
always @ (posedge clk)
begin
    if (ce &amp;&amp; we) begin
        mem[addr] &lt;= data;
    end
end

// reads data from small mem
always @ (posedge clk)
begin
    em_outdata &lt;= mem[addr];
end

endmodule