I have a Verilog module that reads data off a 32-bit bus, one word at a time, and assembles the words into a wider reg that is the input to several other modules. The input bus is a memory bus, so every word comes with an address that identifies where I have to store it.
I use part select to store the words into the reg. Because the word at the lowest address on the bus corresponds to the most-significant word in the reg, I generate the offset for the part select by flipping the bits of the address and then appending three 0 bits to convert it from a byte offset to a bit offset.
This actually works just fine. But because I use the "flip bits then add three zeros" logic in several places, I decided to assign it to a wire, then use the wire as the offset in the part select. However, when I do this, suddenly it doesn't work at all. The first write to word 0 works. But then on the second cycle, the write to word 1 overwrites word 0! Then the following write to word 2 gets saved in word 1. It's as if the wire was a reg and that the results of calculating the offset were not seen until the next cycle. This seems pretty strange since I think of the wire as just being the lines of the address bus run through some NOT gates; whenever the address changes, the offset changes.
I distilled the problem down into the following two examples. I'm using a 3-bit address bus and 8-bit values just to make it easier to see what's going on. Can anyone explain why they exhibit different behavior? I assume that it's due to some idiosyncrasy with the way Verilog schedules blocks, but I can't explain it. Obviously I can just use the working version, or use an array instead of a wide reg, but I think that understanding the reason for this will help me in the future.
Thank you!
Works:
module busif(
input clock,
input [2:0] address,
input read,
input write,
input [7:0] data_in,
output reg [7:0] data_out
);
// 8 bytes of memory (3-bit address).
reg [63:0] memory;
always @(posedge clock) begin
if (write) begin
memory[{~address, 3'b000} +: 8] <= data_in;
end
// (read case omitted)
end
endmodule
Does not work (offset seems to take 1 cycle to propagate through the "wire"):
module busif(
input clock,
input [2:0] address,
input read,
input write,
input [7:0] data_in,
output reg [7:0] data_out
);
// 8 bytes of memory (3-bit address).
reg [63:0] memory;
wire [5:0] offset;
assign offset = {~address, 3'b000};
always @(posedge clock) begin
if (write) begin
memory[offset +: 8] <= data_in;
end
// (read case omitted)
end
endmodule