Here we present a 6-port Register File model. The logical function of a register file is very simple, it is after all the identity function, but once you start adding multiple read and write ports the complexity of modelling such a device seems to grow at an alarming rate.
This register file has 3 write and 3 read ports and a depth of 32 8-bit registers.
When it comes to coding up the design, there are a couple of key choices to make - behavioural or structural coding style. I trod the structural path as this allowed me to play with a variety of VHDL coding techniques. It also seemed that a behavioural approach wouldn't be as challenging...
From the code, you can see that quite a few generate statements are used. Sometimes the generate loop uses an array attribute, sometimes it uses a constant for the loop parameter. Many of the internal signals required the definition of their own data type. This was a synthesis tool constraint rather than a modelling decision, but that's another story. I also decided to mix multi-dimensional array indexing approaches. The register file ports and the latch actual signals are 2-index arrays whereas most of the other signals are arrays of vectors.
To implement the register file in a reasonably efficient way, I opted to use latches as the storage elements rather than flip-flops. There is no write address port conflict arbitration as yet.
You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).
library IEEE; use IEEE.std_logic_1164.all; use work.reg_file_pkg.all; use work.components.all; entity reg_file_32_8_6port is generic ( num_write_ports : integer := 1; num_read_ports : integer := 1; num_bidirectional_ports : integer := 0 ); port ( write : in std_logic_vector(((num_write_ports-1) + num_bidirectional_ports) downto 0); read : in std_logic_vector((( num_read_ports-1) + num_bidirectional_ports) downto 0); address_write : in std_logic_2D_array(0 to 2, 4 downto 0); address_read : in std_logic_2D_array(0 to 2, 4 downto 0); data_in : in std_logic_2D_array(0 to 2, 7 downto 0); data_out : out std_logic_2D_array(0 to 2, 7 downto 0) ); end reg_file_32_8_6port; architecture modular of reg_file_32_8_6port is constant depth : integer := 32; constant data_width : integer := 8; constant num_input_ports : integer := num_write_ports + num_bidirectional_ports; constant num_output_ports : integer := num_read_ports + num_bidirectional_ports; type select_bus is array (0 to num_input_ports-1) of std_logic_vector(depth-1 downto 0); -- 3 busses, 32 bits wide type address_write_bus is array (0 to num_input_ports-1) of std_logic_vector(log_2(depth)-1 downto 0); type address_read_bus is array (0 to num_output_ports-1) of std_logic_vector(log_2(depth)-1 downto 0); type data_write_bus is array (0 to num_input_ports-1) of std_logic_vector(data_width-1 downto 0); type data_read_bus is array (0 to num_output_ports-1) of std_logic_vector(data_width-1 downto 0); signal data_WR_select : select_bus; signal addr_WR : address_write_bus; signal addr_RD : address_read_bus; signal WE_WR : std_logic_vector(0 to num_input_ports-1); signal OE_RD : std_logic_vector(0 to num_output_ports-1); signal data_WR : data_write_bus; signal latch_array_in : std_logic_2D_array(0 to depth-1, data_width-1 downto 0); signal latch_array_out : std_logic_2D_array(0 to depth-1, data_width-1 downto 0); signal mux_out : data_read_bus; signal en : std_logic_vector(0 to depth-1); begin s2: WE_WR <= not write; s3: OE_RD <= read; g3: for i in 0 to num_input_ports-1 generate g4: for j in 0 to log_2(depth)-1 generate addr_WR(i)(j) <= address_write(i, j); end generate g4; decode_addr_WR: demux_generic generic map ( in_width => log_2(depth) ) port map ( a => addr_WR(i), enable => WE_WR(i), y => data_WR_select(i) ); end generate g3; g5: for i in data_in'RANGE(1) generate g6: for j in 0 to data_width-1 generate data_WR(i)(j) <= data_in(i, j); end generate g6; end generate g5; data_mux_selects: process (data_WR, data_WR_select) variable enable : std_logic_vector(0 to depth-1); begin for k in 0 to depth-1 loop for i in 0 to data_width-1 loop latch_array_in(k,i) <= data_WR(0)(i); end loop; enable(k) := '0'; for j in 0 to num_input_ports-1 loop enable(k) := data_WR_select(j)(k) or enable(k); end loop; en(k) <= not enable(k); for j in 0 to num_input_ports-1 loop if data_WR_select(j)(k) = '1' then for i in 0 to data_width-1 loop latch_array_in(k,i) <= data_WR(j)(i); end loop; end if; end loop; end loop; end process; g0: for i in 0 to depth-1 generate g1: for j in 0 to data_width-1 generate latch_instance: d_latch port map ( d => latch_array_in(i,j), enable => en(i), q => latch_array_out(i,j) ); end generate g1; end generate g0; g2: for i in 0 to num_output_ports-1 generate g8: for j in address_read'RANGE(1) generate addr_RD(i)(j) <= address_read(i, j); end generate g8; op_mux: mux_generic generic map ( data_width => data_width, funnel_factor => depth ) port map ( a => latch_array_out, sel => addr_RD(i), y => mux_out(i) ); end generate g2; gi_mux: for i in 0 to num_output_ports-1 generate gj_data: for j in 0 to data_width-1 generate data_out(i,j) <= mux_out(i)(j); end generate gj_data; end generate gi_mux; end modular;
To download the VHDL source code for this model, click here.