8

I am trying to write a register file in VHDL. The file contains 16 64-bit registers. Each cycle, two registers are read and one register is written (given that writing is enabled). There should be a data bypass (forwarding) so that the value just written is forwarded directly to the output if we are reading and writing to/from the same register in a single cycle.

My idea was to write on the rising edge and read on the falling edge of the clock in order to complete this in one cycle. However, my design isn't working (not that I expected it to since I don't believe that checking for a falling edge within an if block which checks for a rising edge would work as expected).

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity register_file is   
    port
    (
    outA          : out std_logic_vector(63 downto 0);
    outB          : out std_logic_vector(63 downto 0);
    input         : in  std_logic_vector(63 downto 0);
    writeEnable   : in std_logic;
    regASel       : in std_logic_vector(5 downto 0);
    regBSel       : in std_logic_vector(5 downto 0);
    writeRegSel   : in std_logic_vector(5 downto 0);
    clk           : in std_logic
    );
end register_file;

architecture behavioral of register_file is
type registerFile is array(0 to 15) of std_logic_vector(63 downto 0);
signal registers : registerFile;
begin

    regFile: process(clk)
    begin
        if rising_edge(clk) then 
            if(writeEnable = '1') then
                registers(to_integer(unsigned(writeRegSel))) <= input;
            end if;
            if falling_edge(clk) then
                outA <= registers(to_integer(unsigned(regASel)));
                outB <= registers(to_integer(unsigned(regBSel)));
            end if;
        end if;
        if falling_edge(clk) then
                outA <= registers(to_integer(unsigned(regASel)));
                outB <= registers(to_integer(unsigned(regBSel)));
        end if;
    end process;
end behavioral;

Any help would be appreciated.

audiFanatic
  • 2,296
  • 8
  • 40
  • 56

2 Answers2

9

The submitted VHDL code has construction with:

...
if rising_edge(clk) then
  ...
  if falling_edge(clk) then
  ...

This will leave dead code, since both rising_edge and falling_edge can't be true at the same time. Also, the idea of using both rising and falling edge will often cause design and synthesis problems.

For best timing, and ease of design and synthesis constraining, I will suggest use of rising edge only, unless it is mandatory to use both rising and falling edge.

With bypass of write data for read A and B in the same cycle, the register file may look like:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity register_file is
  port(
    outA        : out std_logic_vector(63 downto 0);
    outB        : out std_logic_vector(63 downto 0);
    input       : in  std_logic_vector(63 downto 0);
    writeEnable : in  std_logic;
    regASel     : in  std_logic_vector(3 downto 0);
    regBSel     : in  std_logic_vector(3 downto 0);
    writeRegSel : in  std_logic_vector(3 downto 0);
    clk         : in  std_logic
    );
end register_file;


architecture behavioral of register_file is
  type registerFile is array(0 to 15) of std_logic_vector(63 downto 0);
  signal registers : registerFile;
begin
  regFile : process (clk) is
  begin
    if rising_edge(clk) then
      -- Read A and B before bypass
      outA <= registers(to_integer(unsigned(regASel)));
      outB <= registers(to_integer(unsigned(regBSel)));
      -- Write and bypass
      if writeEnable = '1' then
        registers(to_integer(unsigned(writeRegSel))) <= input;  -- Write
        if regASel = writeRegSel then  -- Bypass for read A
          outA <= input;
        end if;
        if regBSel = writeRegSel then  -- Bypass for read B
          outB <= input;
        end if;
      end if;
    end if;
  end process;
end behavioral;

Note that the "address" in *Sel are reduced to 4 bit only, to match the 16 required entries in the register file, as Daniel Kamil Kozar also points out.

There is no checking for X values in simulation, but this can be added with Is_X function, if required.

Morten Zilmer
  • 15,586
  • 3
  • 30
  • 49
1

In the vast majority of cases you cannot have a rising edge and a falling edge in a process, as there are no items within (most) devices you will target that can respond on both edges. (One exception is the IO flipflops in devices which support double-data-rate IO).

If you want to have a bypass, then code it explicitly within a normal rising-edge process:

outa <= registers(to_integer(...etc));
if write_enable = '1' and regAsel = writeregsel then 
   outa <= input;
end if;
-- similar for regb  

Also, why not make your regsel inputs of integer type, or at least unsigned - given that they definitely represent a number (not just an arbitrary bag of bits, which is the case for your data IO vectors)?

Martin Thompson
  • 16,395
  • 1
  • 38
  • 56
  • Using `integer` as type for regsel will remove the possibility of undefined (`X`) values in simulation, with the risk of hiding problems with undefined values that may otherwise be seen at simulation time. Using `unsigned` allows undefined values, but the question is if the modules that instantiates the register file considers the regsel as anything but a bag of bits. So using `std_logic_vector` is a generic approach which postpones the conversion until specific context. – Morten Zilmer Nov 14 '13 at 20:46
  • @MortenZdk - you point to *a* valid design approach. I like to keep things as concrete as possible from as high up as possible. I can't envisage a design where something selecting a register would be other than a number personally, but each to their own :) The pedant in me is also forced to point out that `X` is not undefined, it's a *conflicting* driver (which would fail at elaboration time if `integer` was used). `U` is an undefined (or technically "uninitialised") value - I'm sure *you* knew that, but I'll point it out for future readers :) – Martin Thompson Nov 15 '13 at 08:59