Addition in verilog: simulation doesn't match synthesis

I come from very predominant background in VHDL and have recently been doing a lot more coding in SystemVerilog. I've noticed a couple of strange behaviours that I don't entirely understand.

Summary

In the following example, I am performing an addition of several signals. I know that this is particularly inefficient (and probably bad style), but I am using it to demonstrate the type of issue I've run into recently.

// Main add (inside synchronous process)
// all signals are 32b logic or reg (except for 64, which is a define)
dma_ins.dest    <= sdram_addr + slice_address + slice_offset - 64;
0x3800 0000  0x0001 0000     0x0000 0050


When I run this add in simulation, I get the correct result. In synthesis, it's incorrect.

Simulation: 0x38010010
Synthesis: 0x38008008

The synthesis value is one shift to the right (0x8008 instead of 0x10010).

In order to determine the synthesis value, I am writing all four signals to debug registers. The DMA also ends up at the wrong address in the SDRAM in hardware.

In order to fix this issue, I did the following:

dma_ins.dest        <= sdram_addr;
dma_ins.dest[26:15] <= queue_prop_new[0].contig_head; // non-shifted versions of slice address
dma_ins.dest[14:4]  <= queue_prop_new[0].contig_hoffset - 4; // and slice offset


I'm probably missing something in regards to the SystemVerilog type definitions, but I'm not sure what. I've added some details below.

Detail

The signals being added together are defined as follows:

// SDRAM ADDR (synchronus process, 32b reg)
sdram_addr <= 32'h38000000

// SLICE ADDRESS (asynchronus combinitorial assign)
// slice address: 32b logic
// queue_prop_new[0].contig_head: 12b logic: value of 0x02
// 32768 is a define that has been substituted.
assign slice_address = (queue_prop_new[0].contig_head <<($clog2(32768)-1)); // (slice_address = 0x02 << 15 = 0x10000) // SLICE OFFSET // slice_offset: 32b logic // queue_prop_new[0].contig_hoffset: 11b logic: value of 0x05 // 16 is a substituted define assign slice_offset = (queue_prop_new[0].contig_hoffset<<($clog2(16)-1));
// (slice_offset = 0x05 << 4 = 0x50)

• As I've been writing this, I've actually got a hunch as to what the reason is: adding two 32-bit numbers produces a 33-bit number and my simulator is crap. – stanri Jul 17 '15 at 18:28
• If the addition overflows but then the subtraction corrects this, underflowing, bringing it back in range you have the potential to be ok. I would try with 31 bit values and see if you still see the issue. the simulator might be struggling with values greater than 32 bits (not overflow/underflowing correctly. – pre_randomize Jul 17 '15 at 19:16
• No, it has nothing to do with 32 bits width. Obviously, with the numbers given in your example, nothing is overflowing. Besides, the simulation is correct and the synthesis is wrong, so it has nothing to do with your simulator, either. – Dave Tweed Jul 17 '15 at 19:24

1 Answer

The expressions ($clog2(32768)-1) and ($clog2(16)-1) look suspicious to me — don't they yield 14 and 3, respectively, rather than the 15 and 4 that you desire?

But I can't explain why the simulator is getting the "correct" result.

• the $clog2 is not the same as log2. the$clog2(n) produces the number of bits necessary to store the value n. As a result, \$clog2(16) = 5. I subtract 1 to get 4, the actual log2 value. Additionally, I have looked up the intermediate signals (slice_) in hw and they produce the correct values (with the right shifts). – stanri Jul 17 '15 at 19:35
• No, it doesn't. clog2()` is an abbreviation for "ceiling (log2 ())", and it yields the number of address bits required to address a memory of the given size, not the number of bits required to represent the size itself. When the argument is a power of 2, log2 and clog2 give the same result. – Dave Tweed Jul 17 '15 at 19:40
• Huh, you're right. I must have misunderstood. I assumed my understanding was correct because that's what the simulation tool produced. – stanri Jul 17 '15 at 19:48