simulation and synthesis behaves differently

R

rekz

Guest
Has anyone experienced such cases? I run the simulation on my datapath
that I build and has all the functionality working fine, however when
I synthesize it and put it into my FPGA board the branch breaks
down... how is this happening? What should I do to resolve this?
 
On Wed, 5 May 2010 11:26:31 -0700 (PDT), rekz <aditya15417@gmail.com>
wrote:

Has anyone experienced such cases? I run the simulation on my datapath
that I build and has all the functionality working fine, however when
I synthesize it and put it into my FPGA board the branch breaks
down... how is this happening? What should I do to resolve this?
This question relates to the same design that you showed in the
earlier thread "problem with rising clock edge", right?

In that design, you used blocking assignment to a variable
whose value was used outside the clocked always block.
This is well known to be a great way to get the kind of
mismatch you describe. In fact, you've already seen
the problem yourself - you were seeing things updated
one clock cycle later than you wanted. My guess is that
in hardware you *always* get the update one cycle
later, whereas in simulation you were able to find
some hack that gave you earlier updates.

A Google search for "blocking nonblocking Verilog"
will turn up most of the standard discussions and
papers that you should read before you try to do
anything else.
--
Jonathan Bromley
 
On May 5, 2:46 pm, Jonathan Bromley <s...@oxfordbromley.plus.com>
wrote:
On Wed, 5 May 2010 11:26:31 -0700 (PDT), rekz <aditya15...@gmail.com
wrote:

Has anyone experienced such cases? I run the simulation on my datapath
that I build and has all the functionality working fine, however when
I synthesize it and put it into my FPGA board the branch breaks
down... how is this happening? What should I do to resolve this?

This question relates to the same design that you showed in the
earlier thread "problem with rising clock edge", right?

In that design, you used blocking assignment to a variable
whose value was used outside the clocked always block.
This is well known to be a great way to get the kind of
mismatch you describe.  In fact, you've already seen
the problem yourself - you were seeing things updated
one clock cycle later than you wanted.  My guess is that
in hardware you *always* get the update one cycle
later, whereas in simulation you were able to find
some hack that gave you earlier updates.

A Google search for "blocking nonblocking Verilog"
will turn up most of the standard discussions and
papers that you should read before you try to do
anything else.
--
Jonathan Bromley
After hours of debugging I finally found out what the problem is.
I have the following code for a register file:

always @(negedge Clk) begin
if (RegWrite) begin
RF[WriteRegister] = WriteData;
end

ReadData1 = RF[ReadRegister1];
ReadData2 = RF[ReadRegister2];
end

Problem is that when we want to synthesize register we need to use <instead of =, otherwise it will trim all the register. Now in the code
above the order of evaluation is really important. I want to write to
the register first before reading it. So how am I able to synthesize
it then if this is the case, aren't there any alternative to this??
Please help.
 
On May 5, 11:47 pm, rekz <aditya15...@gmail.com> wrote:

I have the following code for a register file:

always @(negedge Clk) begin
   if (RegWrite) begin
      RF[WriteRegister] = WriteData;
   end

      ReadData1 = RF[ReadRegister1];
      ReadData2 = RF[ReadRegister2];
end

Problem is that when we want to synthesize register we need to use <> instead of =, otherwise it will trim all the register.
That is NOT TRUE. I have no idea what makes you believe that.

Now in the code
above the order of evaluation is really important. I want to write to
the register first before reading it. So how am I able to synthesize
it then if this is the case, aren't there any alternative to this??
There are some critical errors in your code. You can't just write
random software and expect it to synthesise correctly.

What hardware structure do you envisage? Any edge triggered
register will surely read back the OLD value. Some RAM structures
support write-first behaviour by careful manipulation of their
internal asynchronous clocks. But if you are making a register file
from edge-triggered flipflops, you will need to get write-first in
a different way. One possibility is to use a feed-forward path:

always @(nedgedge Clk) begin : ThreePortRegisters
// Register file storage declared locally
reg [DATA_WIDTH-1:0] RF [0:NUM_REGS-1];
// Read back the old values
// (but this may be changed if there's a read/write address
clash)
ReadData1 <= RF[ReadRegister1];
ReadData2 <= RF[ReadRegister2];
// update the internal flipflops
if (RegWrite) begin
RF[WriteRegister] = WriteData;
// Write-forwarding if there's a read/write address collision
if (WriteRegister == ReadRegister1)
ReadData1 <= WriteData;
if (WriteRegister == ReadRegister2)
ReadData2 <= WriteData;
end
end

Obviously, though, the feed-forward path is expensive in hardware.
Some synthesis tools do this automatically if the target hardware
does not support write-first behaviour, but you describe a RAM
with such behaviour (as I describe below).

Now, before we move on to describing a RAM block (for FPGA) with
this behaviour, let's take a careful look at my use of <= and assignments.

Variables ReadData1 and ReadData2 will, of course, be used
by other code outside this clocked always-block. Consequently
they must be declared outside the block, at the top level of
the module, and THEY MUST BE WRITTEN USING <= NONBLOCKING ASSIGNMENT.
The reasoning behind this has been so well rehearsed, here and
elsewhere, that I don't intend to repeat it. Just do it. If you
get this wrong, your code will misbehave in various subtle ways
and you will get absolutely no sympathy from me now that I've
told you the right way to do it.

However, for the register file RF[] itself, it's a rather
different story. No other code anywhere in the design can
read RF[] directly; the only way to read it is through the
read access mechanism in this code. Consequently it's a
good idea to hide RF[] inside the always-block by:
- labelling the begin-end;
- declaring RF[] locally inside that begin-end.

Once you've done that, it is OK to write to RF[] using
blocking = assignment, but you must be aware that such
assignments update immediately like software variables.
That's why I did the read operation FIRST in my code.
Those registers will not be trimmed away by synthesis.

We can exploit that immediate-update behaviour to model
write-first memory:

always @(nedgedge Clk) begin : ThreePortRegistersVersion2
// Register file storage declared locally
reg [DATA_WIDTH-1:0] RF [0:NUM_REGS-1];
// update the internal flipflops
if (RegWrite) begin
RF[WriteRegister] = WriteData;
end
// Read will now get the updated value
ReadData1 <= RF[ReadRegister1];
ReadData2 <= RF[ReadRegister2];
end

This will simulate correctly, but synthesis is more tricky. Some
tools will identify the implied behaviour and map it on to an
appropriate RAM macro if one is available. Other tools will
identify the behaviour correctly, but will create the same
comparators and write-forwarding multiplexer that you would
get from my first design. Yet other tools may choke on it
completely. Look at your vendor's documentation - but
also ask yourself whether your target hardware supports this
behaviour in its RAM structures. If not, it may be better to
rework the architecture of your design so that it can work
correctly with read-before-write registers.

--
Jonathan Bromley
 

Welcome to EDABoard.com

Sponsor

Back
Top