2
\$\begingroup\$

I want to implement a data register to store up to 256 8 bit chunks (VHDL). I also need to to be able to access and modify these values from multiple vhdl modules. One module will access the register to modify the values from the spi port. Then another module that implements the EPP protocol to communicate to the PC will shove out the data.

Right now I just have the signal that implements the register in one of my modules:

type type_dataRegisterWithAddress is array (0 to generic_numRegisters-1) of std_logic_vector(7 downto 0); 
signal dataRegister: type_dataRegisterWithAddress;

I am not sure of the technical details of a register but I am just storing logic vectors and accessing with a to_integer(address).

I do not know how I would share this to another module as I can't just put in the port map since it is an array. Even if it was in the top level module I still do not know how I would share it.

What is the best method to create some registers that can be accessed from many modules?

EDIT: To answer some of Dave's questions...

I kinda wanted to implement it in code like I am doing since this is a small storage and I am not actually going to use 256, it is just that the EPP interface can have 256 addresses. But you do bring up a good point.

I am using the Basys2 250k board which has 72Kbits of dual-port block ram. I have only ever used it using the IP Core function in this tutorial/module. I think this may be the best thing to use.

Now that I have the correct term. What is the your personal approach to shared memory?

\$\endgroup\$
  • \$\begingroup\$ Shared memory is a very broad topic, and there are many approaches you can take, which depend on the number of ports required, the bandwidth on each port and the allowed access latency. Some approaches will allow the use of dual-port "block RAM" modules that are available on many FPGAs. There is no single "best method". It sounds like you're working on an FPGA-based project, so if you could specify the part you're using, along with some answers to the above questions, then we might be able to suggest an appropriate implementation. \$\endgroup\$ – Dave Tweed Dec 31 '12 at 0:52
  • \$\begingroup\$ @DaveTweed Thanks for the added clarity check. I updated the post. \$\endgroup\$ – MLM Dec 31 '12 at 1:28
1
\$\begingroup\$

First of all, the fact that it is an array does not prevent you from using it in the port declaration.

For example, you could make a package that contains the common types used in your project:

-- common_types.vhd
-- This package contains common types used in your project.

library ieee;
use ieee.std_logic_1164.all; 

package common_types is

    -- Example use of slv8_array: 
    -- signal a : slv8_array(0 to 3);
    type slv8_array  is array(integer range <>) of std_logic_vector( 7 downto 0); 

end package;

Inside any module that wants to use any of your types, include the package by writing:

use work.common_types.all;

Then in your entity declaration, you can now use it in your port or generic declaration:

entity e is
port (      
    a   : out slv8_array(0 to 3)
);
end entity;

Secondly, regarding sharing a resource (like memory):

  • If you are using block ram, it usually supports dual port, so you have at least two ports for sharing and they are truly simultaneous.
  • If you need more ports, you have to come up with some kind of resource arbitration scheme. This arbiter will be the front end to all modules that need access to the resource. This means that the arbiter has access to the real port to the resource, but declares multiple other ports for the clients that wish to use it. It then grants access to each of them, one at a time, according to some kind of resource sharing algorithm, which can be as simple as a round robin.

For an example, albeit in verilog, see Altera's offering (source code is freely downloadable) http://www.altera.com/support/kdb/solutions/rd11252011_496.html

The MPFE arbiter uses an enhanced version of the weighted round-robin scheme which grants access to successive slave ports in medium sized blocks (up to 64 beats), but has a leaky bucket bandwidth allocation system to distribute the accesses more evenly.

This will probably be overkill for you. If your bandwidth requirements are not great, write your own arbiter that polls a write/read request signal from each client, one at a time, and when it finds an asserted signal then bridges them to the real resource. When the transaction is finished, resume the polling process.

Finally, one last thing to mention in the case of a relatively small register bank, is that for readers you could provide a big mux for each one of them, that way multiple clients could read any register simultaneously, without a need for an arbiter. Handling multiple writers is a bit different though, you'd have a big decoder for each writer, but you would need to resolve collisions. One way would be to establish priority, so writers could write to any register at the same time, but if there is a collision, only one of them would be successful, and it would be up to an upper level to ensure that this is either not a problem, or somehow handled. This uses up a lot of combinational logic, so it would only make sense for small register banks.

\$\endgroup\$
  • \$\begingroup\$ Looks like I will have to find a vhdl arbiter with multiple ports. I thought it would be as easy as making a inout declaration and pass the memory to every module but I get a multiple driver error. Thank you for pointing me in the right direction! \$\endgroup\$ – MLM Jan 2 '13 at 3:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.