Register File access problem

  • Thread starter Philipp Richter
  • Start date
P

Philipp Richter

Hi

I have an architecture that implements a register file in the following way:


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity Reg_File is
port (
clk : in std_logic;
rd1_addr : in std_logic_vector(4 downto 0);
rd2_addr : in std_logic_vector(4 downto 0);
rd1_val : out std_logic_vector(31 downto 0);
rd2_val : out std_logic_vector(31 downto 0);
wr_ena : in std_logic;
wr_addr : in std_logic_vector(4 downto 0);
wr_val : in std_logic_vector(31 downto 0)
);
end Reg_File;

architecture syn of Reg_File is

type ram_type is array (0 to 31) of std_logic_vector(31 downto 0);
signal RAM : ram_type;

begin
process (clk)
begin
if (clk'event and clk = '1') then
if (wr_ena = '1') then
RAM(conv_integer(wr_addr)) <= wr_val;
end if;
end if;
end process;

rd1_val <= RAM(conv_integer(rd1_addr));
rd2_val <= RAM(conv_integer(rd2_addr));

end syn;

The problem that I face here, is that I read an undefined value when
rd1_addr or rd2_addr are the same as wr_addr. In other words, I cant
write a register value and read from it at the same time (makes sense
obviously). However, whats the best way to overcome this problem, maybe
writing back the register values at the negative edge of the clock? Is
this still sythesizeable and will run stable on an FPGA or will I face
there some problems? Thanks for any other helpful suggestions.

Philipp
 
M

Mike Treseler

Philipp said:
The problem that I face here, is that I read an undefined value when
rd1_addr or rd2_addr are the same as wr_addr. In other words, I cant
write a register value and read from it at the same time (makes sense
obviously).

Your choices are:

1.Make a controller to arbitrate the writes.
2.Use a synch fifo style memory with separate read a write ports.
3.Use a register stack like this:
http://mysite.verizon.net/miketreseler/stack.vhd


-- Mike Treseler
 
P

Philipp Richter

1.Make a controller to arbitrate the writes.
2.Use a synch fifo style memory with separate read a write ports.
3.Use a register stack like this:
http://mysite.verizon.net/miketreseler/stack.vhd

Alright, option 2 and 3 come not really into question for me as I have
32 registers that I wanna randomly write to and read from. So a FIFO
is not the solution I am looking for. Have to consider option 1, but
I was hoping that I could solve this problem easier.

Thanks
Philipp
 
M

Mike Treseler

Philipp said:
Alright, option 2 and 3 come not really into question for me as I have
32 registers that I wanna randomly write to and read from. So a FIFO
is not the solution I am looking for. Have to consider option 1, but
I was hoping that I could solve this problem easier.

If you do let me know.
I would like a choice 4 also ;)

-- Mike Treseler
 
D

David Spencer

Philipp Richter said:
Hi

I have an architecture that implements a register file in the following
way:


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity Reg_File is
port (
clk : in std_logic;
rd1_addr : in std_logic_vector(4 downto 0);
rd2_addr : in std_logic_vector(4 downto 0);
rd1_val : out std_logic_vector(31 downto 0);
rd2_val : out std_logic_vector(31 downto 0);
wr_ena : in std_logic;
wr_addr : in std_logic_vector(4 downto 0);
wr_val : in std_logic_vector(31 downto 0)
);
end Reg_File;

architecture syn of Reg_File is

type ram_type is array (0 to 31) of std_logic_vector(31 downto 0);
signal RAM : ram_type;

begin
process (clk)
begin
if (clk'event and clk = '1') then
if (wr_ena = '1') then
RAM(conv_integer(wr_addr)) <= wr_val;
end if;
end if;
end process;

rd1_val <= RAM(conv_integer(rd1_addr));
rd2_val <= RAM(conv_integer(rd2_addr));

end syn;

The problem that I face here, is that I read an undefined value when
rd1_addr or rd2_addr are the same as wr_addr. In other words, I cant write
a register value and read from it at the same time (makes sense
obviously). However, whats the best way to overcome this problem, maybe
writing back the register values at the negative edge of the clock? Is
this still sythesizeable and will run stable on an FPGA or will I face
there some problems? Thanks for any other helpful suggestions.

Philipp

Do you have to support asynchronous reads of the registers? If not, then
move the assignments for 'rd1_val' and 'rd2_val' into the process. The
behavior should then be consistent. Of course, you don't have any form of
reset so simulation will show an undefined for any register you read before,
or while, writing it.

Also, you should avoid using "(clk'event and clk = '1')" and instead use
"(rising_edge(clk))" which will simulate correctly for all possible values
of clk. For example, using the original form would fail to recognize a
'0' -> 'H' transition as a valid clock, but would think the non-transition
'H' -> '1' was a valid edge.
 
P

Philipp Richter

Do you have to support asynchronous reads of the registers? If not, then
move the assignments for 'rd1_val' and 'rd2_val' into the process. The
behavior should then be consistent. Of course, you don't have any form of
reset so simulation will show an undefined for any register you read before,
or while, writing it.

Thanks for the feedback David, but unfortunately I need asynchronous
reads. The approach with writting on the negative edge of the clock is
bollocks?

Any other good ideas out there ;) ?

Cheers,
Philipp
 
P

Philipp Richter

KJ said:
1. Since your design has three ports (one for writing memory, two for
reading memory) it most likely won't be implemented in internal FPGA
memory anyway. Instead it will be implemented in flops which then
makes the entire memory totally accessible...is that really your
intention?

Yes, to obtain the desired behaviour I need 2 asynchronous read
and 1 synchronous write port.

3. The case where the read and write addresses happen to match can be
handled in your code like this:
rd1_val <= wr_val when (wr_addr = rd1_addr) else
RAM(conv_integer(rd1_addr));
rd2_val <= wr_val when (wr_addr = rd2_addr) else
RAM(conv_integer(rd2_addr));

Thats sounds almost too easy ;). Have to try it tomorrow and see if it
works and then at a later stage if i can synthesize the stuff correctly
 
K

KJ

Thanks for the feedback David, but unfortunately I need asynchronous
reads. The approach with writting on the negative edge of the clock is
bollocks?

1. Since your design has three ports (one for writing memory, two for
reading memory) it most likely won't be implemented in internal FPGA
memory anyway. Instead it will be implemented in flops which then
makes the entire memory totally accessible...is that really your
intention?

2. Using both edges of the clock is generally a sign of a poor
design...'specially if you have no good reason.

3. The case where the read and write addresses happen to match can be
handled in your code like this:
rd1_val <= wr_val when (wr_addr = rd1_addr) else
RAM(conv_integer(rd1_addr));
rd2_val <= wr_val when (wr_addr = rd2_addr) else
RAM(conv_integer(rd2_addr));

Kevin Jennings
 
A

Andy

In Xilinx it'll be implemented in flops, but not for that reason.

The BlockRams require a clock cycle before read data is available (I believe
it's the address that is registered, rather than the output data, but the effect
is a cycle delay. For maximum clock rate you want to register both, but that's
another matter)

So if you genuinely need asynch read, you will be using the FPGA fabric (and
quite a lot of it!) This will also keep your maximum clock rate relatively low.

The three port design lends itself perfectly to BlockRam implementation in other
respects. You simply use two BlockRams, and tie the write ports together to
duplicate the data. That leaves two independent read ports. (Multiple write
ports are a bit more tricky!)

KJ's other comments I agree with.

- Brian

Xilinx has distributed single and dual port rams in the fabric that
have async read. Up to 32 bits single port and 16 bits dual port.

Otherwise, like Brian said, use two arrays, both written to
simultaneously using write address. For reading, index the first array
with one read address, and index the second array with the other read
address.

Andy
 
T

Thomas Stanka

Hi,

I have an architecture that implements a register file in the following way: [..]
signal RAM : ram_type;

why ram_type? Area usage problem?
Your code should work with FF. If it works with RAM deends on the
actual implementation of the RAM, but I know no RAM with three
independant ports, so I guess FF are be generated anyway.

To use the design with FF I would use a reset.

Are the values realy undefined ('X'), or just uninitialised ('U'),
because you read before first write operation. First would indicate a
Setup-Hold violation.

bye Thomas
 
P

Philipp Richter

Your code should work with FF. If it works with RAM deends on the
actual implementation of the RAM, but I know no RAM with three
independant ports, so I guess FF are be generated anyway.

Yes and the next thing is that I need asynchronous reads. This implies
that I need distributed RAM as BRAM is only possible with synchronous
reads.
To use the design with FF I would use a reset.

Are the values realy undefined ('X'), or just uninitialised ('U'),
because you read before first write operation. First would indicate a
Setup-Hold violation.

At the moment I have initialised them to some values as follows:

type ram_type is array (0 to 31) of std_logic_vector(31 downto 0);
signal RAM : ram_type :=
(
B"00000000000000000000000000000000",
B"00000000000000000000000000000001",
....
);

I assume this works just for simulation as I cant initialise distributed
RAM cells if I am not completly wrong. In other words, I have to use
probably the rst signal to initialise the Reg FIle contents to ZERO

Cheers,
Philipp
 
D

David Spencer

Philipp Richter said:
Yes and the next thing is that I need asynchronous reads. This implies
that I need distributed RAM as BRAM is only possible with synchronous
reads.


At the moment I have initialised them to some values as follows:

type ram_type is array (0 to 31) of std_logic_vector(31 downto 0);
signal RAM : ram_type :=
(
B"00000000000000000000000000000000",
B"00000000000000000000000000000001",
....
);

I assume this works just for simulation as I cant initialise distributed
RAM cells if I am not completly wrong. In other words, I have to use
probably the rst signal to initialise the Reg FIle contents to ZERO

Cheers,
Philipp

Is this register file accessed from an external processor? If so, then you
probably want a reset anyway don't you? If so, the register array will have
to be implemented as flip-flops. It will take 1024 flip-flops which probably
isn't excessive in a mid-size FPGA. The alternative would be to include
extra logic to zero the RAM array after reset.

Apart from the lack of a reset, I still can't see why your original logic
didn't work though.
 
K

KJ

I still can't see why your original logic
didn't work though.

Because Philipp's definition of 'working' is that the read data must
show up at the *same time* as it is being written into memory, not one
clock cycle later when it has been stored away.

KJ
 
D

David Spencer

KJ said:
Because Philipp's definition of 'working' is that the read data must
show up at the *same time* as it is being written into memory, not one
clock cycle later when it has been stored away.

KJ

Has he said that? I took his original post to mean that he must get valid
data if a register is read at the same time as being written, but in most
register bank type situations the previous contents would be considered as
valid and accpetable data.

I think Philipp needs to tell us a little bit more about the application
because I think we are only seeing a tiny portion without enough context to
understand the bigger picture.
 
P

Philipp Richter

Because Philipp's definition of 'working' is that the read data must
show up at the *same time* as it is being written into memory, not one
clock cycle later when it has been stored away.

Yes, exactly. However, for me the problem was that this must also cause
some exception when I try to read from the same register as to which I
am writing...
 
P

Philipp Richter

Has he said that? I took his original post to mean that he must get valid
data if a register is read at the same time as being written, but in most
register bank type situations the previous contents would be considered as
valid and accpetable data.

Ah alright, I was pretty sure that this will cause some trouble if
writing and reading to the same destination at the same time. But its
easier for my implementation if the value that is written IS AVAILABLE
at the same time to be read out again of the register file. So I like
KJ approach. Works fine so far in the simulation ;)
 

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

No members online now.

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top