Dual Port RAM Inference

R

rickman

My code is virtually this same thing. The tool tells me I am trying
to infer two ports using distributed memory which it can't do.

Rick
 
M

Mark

I know I'm a little late on this thread, but offer my two cents,
on what we use, and a warning as well.

We use dual-port RAMS (same clock) with inference, and don't have
trouble. It's in verilog, and it's READ_FIRST. So two strikes
against it for what you're looking for Rick. (you want VHDL, and
WRITE_FIRST, I beleive). We call this our "mem2rw1clk" module.

But here's what we do (minus header/etc):

always @( posedge clk )
begin
if( en0 )
begin
if( wren0 )
mem[ addr0 ] <= wdata0;
rdata0 <= mem[ addr0 ];
end
end

always @( posedge clk )
begin
if( en1 )
begin
if( wren1 )
mem[ addr1 ] <= wdata1;
rdata1 <= mem[ addr1 ];
end
end

So, two almost identical always blocks, operating on the same RAM.
Since we use non-blocking assignments, the READ_FIRST is implied
(correctly by XST).

Works, and we've been using it for many designs no trouble.

Now the warning:

We use almost the EXACT same structure for implementing a pseudo
dual port - i.e. an independant READ port, and a WRITE port
(same clock) "mem1r1w1clk". I.e. the type of memory you'd
use for a synchronous fifo. The logic is again clearly coded for
READ_FIRST.

Well, XST was (sometimes) inferring WRITE_FIRST. So, simulation
vs implementation mismatch. It only mattered in a few places
we were specifically ALWAYS reading the same location as we
were writing in the same cycle. You get quite different results.
Spent 2-3 weeks on the bench figuring out this one.

So - check the XST report to make sure it's inferring the
correct READ_FIRST vs. WRITE_FIRST behaviour. XST can get things
wrong here.


Regards,

Mark
 
P

peter

I know I'm a little late on this thread, but offer my two cents,
on what we use, and a warning as well.

We use dual-port RAMS (same clock) with inference, and don't have
trouble.  It's in verilog, and it's READ_FIRST.  So two strikes
against it for what you're looking for Rick.  (you want VHDL, and
WRITE_FIRST, I beleive).  We call this our "mem2rw1clk" module.

But here's what we do (minus header/etc):

always @( posedge clk )
begin
  if( en0 )
  begin
    if( wren0 )
      mem[ addr0 ] <= wdata0;
    rdata0 <= mem[ addr0 ];
  end
end

always @( posedge clk )
begin
  if( en1 )
  begin
    if( wren1 )
      mem[ addr1 ] <= wdata1;
    rdata1 <= mem[ addr1 ];
  end
end

So, two almost identical always blocks, operating on the same RAM.
Since we use non-blocking assignments, the READ_FIRST is implied
(correctly by XST).

Works, and we've been using it for many designs no trouble.

Now the warning:

We use almost the EXACT same structure for implementing a pseudo
dual port - i.e. an independant READ port, and a WRITE port
(same clock) "mem1r1w1clk".    I.e. the type of memory you'd
use for a synchronous fifo.  The logic is again clearly coded for
READ_FIRST.

Well, XST was (sometimes) inferring WRITE_FIRST.  So, simulation
vs implementation mismatch.  It only mattered in a few places
we were specifically ALWAYS reading the same location as we
were writing in the same cycle. You get quite different results.
Spent 2-3 weeks on the bench figuring out this one.

So - check the XST report to make sure it's inferring the
correct READ_FIRST vs. WRITE_FIRST behaviour.  XST can get things
wrong here.

Regards,

Mark

I am surprised about the interest in write_first vs read_first.
The read output during a write operation came really about as an
afterthought. ("It's easy, the port is already there, so it costs
nothing").
But why do you want to read from the same location that you are
writing to?
Especially when you are reading what you already know, since you
simultaneously are writing it (which was the original mode).
Then we found that read-before-write was an easy modification, and
more valuable.
But still: why do you read from the write address, when you have a
separate read port with its own dedicated addressing available?

But, judging from the interest in this thread, it seems to be
valuable.
Peter Alfke
 
S

Sandro

My code is virtually this same thing.  The tool tells me I am trying
to infer two ports using distributed memory which it can't do.

Rick,
I don't know! It works fine to me (webpack ISE 10.1.03 - linux).

Did you try to use shared variable ? In your previous example
I saw only "<=" instead of ":=" to "assign" a shared variable...

Sandro
 
M

Mark

I am surprised about the interest in write_first vs read_first.
The read output during a write operation came really about as an
afterthought. ("It's easy, theportis already there, so it costs
nothing").
But why do you want to read from the same location that you are
writing to?

For "READ_FIRST" it makes sense. Your reading an old value and
the same time your updating a new value. For us it's an image
processing algorithm, where pixels are going into a line buffer.
We needed line[n-1] pixel value now (the READ data), along with
the current value (the WRITE data). On the next line the
previously written data is now line[n-1], repeat. So the
address of the READ, and WRITE are ALWAYS the same (the column
address). So (depending on how you count things) this consumes
one RAM port.
Especially when you are reading what you already know, since you
simultaneously are writing it (which was the original mode).
Then we found that read-before-write was an easy modification, and
more valuable.

I agree, "WRITE_FIRST" has more limited utility. I didn't know
the history that was the only available mode previously.
But still: why do you read from the write address, when you have a
separate readportwith its own dedicated addressing available?

Yes, Xilinx has "True Dual Port", but I'd rather
code to the minimum that I need in tech independant manner,
and let the tool build from what's available.

If the tool can't build it I'd rather it barf and quit, rather
than just build something willy-nilly that doesn't match the
description. (okay, a bit snarky - I guess I'm still a little
sore over all that time in the lab debugging an XST issue...)

--Mark
 
P

peter

I am surprised about the interest in write_first vs read_first.
The read output during a write operation came really about as an
afterthought. ("It's easy, theportis already there, so it costs
nothing").
But why do you want to read from the same location that you are
writing to?

For "READ_FIRST" it makes sense.  Your reading an old value and
the same time your updating a new value.  For us it's an image
processing algorithm, where pixels are going into a line buffer.
We needed line[n-1] pixel value now (the READ data), along with
the current value (the WRITE data).  On the next line the
previously written data is now line[n-1], repeat.  So the
address of the READ, and WRITE are ALWAYS the same (the column
address).  So (depending on how you count things) this consumes
one RAM port.
Especially when you are reading what you already know, since you
simultaneously are writing it (which was the original mode).
Then we found that read-before-write was an easy modification, and
more valuable.

I agree, "WRITE_FIRST" has more limited utility.  I didn't know
the history that was the only available mode previously.
But still: why do you read from the write address, when you have a
separate readportwith its own dedicated addressing available?

Yes, Xilinx has "True Dual Port", but I'd rather
code to the minimum that I need in tech independant manner,
and let the tool build from what's available.

If the tool can't build it I'd rather it barf and quit, rather
than just build something willy-nilly that doesn't match the
description.  (okay, a bit snarky - I guess I'm still a little
sore over all that time in the lab debugging an XST issue...)

--Mark

Mark, there are clearly several different ways to implement your
design: single port with read-before-write (the most elegant way),
or dual-port with duplicated offset addressing,
or even time-sequenced read-then-write, time permitting.
It is frustrating to know that it can be done, but not be able to do
it.
Maybe you expect the synthesizers to be more versatile and smarter
than they really are.
Peter Alfke
 
P

peter

I am surprised about the interest in write_first vs read_first.
The read output during a write operation came really about as an
afterthought. ("It's easy, theportis already there, so it costs
nothing").
But why do you want to read from the same location that you are
writing to?

For "READ_FIRST" it makes sense.  Your reading an old value and
the same time your updating a new value.  For us it's an image
processing algorithm, where pixels are going into a line buffer.
We needed line[n-1] pixel value now (the READ data), along with
the current value (the WRITE data).  On the next line the
previously written data is now line[n-1], repeat.  So the
address of the READ, and WRITE are ALWAYS the same (the column
address).  So (depending on how you count things) this consumes
one RAM port.
Especially when you are reading what you already know, since you
simultaneously are writing it (which was the original mode).
Then we found that read-before-write was an easy modification, and
more valuable.

I agree, "WRITE_FIRST" has more limited utility.  I didn't know
the history that was the only available mode previously.
But still: why do you read from the write address, when you have a
separate readportwith its own dedicated addressing available?

Yes, Xilinx has "True Dual Port", but I'd rather
code to the minimum that I need in tech independant manner,
and let the tool build from what's available.

If the tool can't build it I'd rather it barf and quit, rather
than just build something willy-nilly that doesn't match the
description.  (okay, a bit snarky - I guess I'm still a little
sore over all that time in the lab debugging an XST issue...)

--Mark

Mark, I listened to Obama's comment that his wife "has the right to
bear (bare) arms". That was a very clever pun, but it would not
translate into any other language.
If you expect your talk to be translated automatically (or even by
humans) into French, German, or Chinese, you have to avoid all such
clever constructs, and go for boring middle-of-the-road statements.
Same with logic design. If you design generically, you miss out on
many subtleties.
This is not meant as an excuse for Xilinx to misunderstand relatively
simple BRAM constructs...
Peter Alfke
 
R

rickman

I know I'm a little late on this thread, but offer my two cents,
on what we use, and a warning as well.
We use dual-port RAMS (same clock) with inference, and don't have
trouble.  It's in verilog, and it's READ_FIRST.  So two strikes
against it for what you're looking for Rick.  (you want VHDL, and
WRITE_FIRST, I beleive).  We call this our "mem2rw1clk" module.
But here's what we do (minus header/etc):
always @( posedge clk )
begin
  if( en0 )
  begin
    if( wren0 )
      mem[ addr0 ] <= wdata0;
    rdata0 <= mem[ addr0 ];
  end
end
always @( posedge clk )
begin
  if( en1 )
  begin
    if( wren1 )
      mem[ addr1 ] <= wdata1;
    rdata1 <= mem[ addr1 ];
  end
end
So, two almost identical always blocks, operating on the same RAM.
Since we use non-blocking assignments, the READ_FIRST is implied
(correctly by XST).
Works, and we've been using it for many designs no trouble.
Now the warning:
We use almost the EXACT same structure for implementing a pseudo
dual port - i.e. an independant READ port, and a WRITE port
(same clock) "mem1r1w1clk".    I.e. the type of memory you'd
use for a synchronous fifo.  The logic is again clearly coded for
READ_FIRST.
Well, XST was (sometimes) inferring WRITE_FIRST.  So, simulation
vs implementation mismatch.  It only mattered in a few places
we were specifically ALWAYS reading the same location as we
were writing in the same cycle. You get quite different results.
Spent 2-3 weeks on the bench figuring out this one.
So - check the XST report to make sure it's inferring the
correct READ_FIRST vs. WRITE_FIRST behaviour.  XST can get things
wrong here.

Mark

I am surprised about the interest in write_first vs read_first.
The read output during a write operation came really about as an
afterthought. ("It's easy, the port is already there, so it costs
nothing").
But why do you want to read from the same location that you are
writing to?
Especially when you are reading what you already know, since you
simultaneously are writing it (which was the original mode).
Then we found that read-before-write was an easy modification, and
more valuable.
But still: why do you read from the write address, when you have a
separate read port with its own dedicated addressing available?

But, judging from the interest in this thread, it seems to be
valuable.
Peter Alfke

That's easy. In my case I am using the ram as a stack, two actually.
Each port has to read whatever was last written because it does *not*
have an independent read port and the read data has to reflect the top
of the stack at all times. The ram is shared as two stacks to save
space since the entire depth of the block ram is not needed.

Thinking that the read port does not need to reflect the last written
data is a very limited perspective. *I* may know what was written,
but whatever is connected to the read port does not know it unless the
read port reflects it.

Rick
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,071
Latest member
MetabolicSolutionsKeto

Latest Threads

Top