3
\$\begingroup\$

I'm practicing for a lab exam and I'm trying to solve one from past years. I feel like I'm doing something wrong because I don't have much experience with VHDL.

Exam question

Write the VHDL code for a D Flip-Flop with asynchronous reset. The D Flip-Flop should include an active-high RESET input and a QN output. A memory cell can use a D Flip-Flop to store one bit in it, and 32 memory cells in one line form a 32-bit register. Write the code for a register that uses 32 of the designed D Flip-Flop as memory cells. The register should include a RESET input, and a QN output. Each row of a RAM corresponds to a word line. Write the code for a 32x32 RAM that uses 32 of the designed 32-bit register as word lines.

I wrote the modules in different files. Here is the code:

DFF.vhd

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY DFF IS
    PORT (
        CLK : IN  STD_LOGIC; -- Clock input
        RST : IN  STD_LOGIC; -- Reset input (active-high)
        D   : IN  STD_LOGIC; -- Data input
        Q   : OUT STD_LOGIC; -- Data output
        QN  : OUT STD_LOGIC  -- Complement of Data output
    );
END DFF;

ARCHITECTURE Behavioral OF DFF IS

    SIGNAL Q_tmp : STD_LOGIC; -- Internal Data output signal
    
BEGIN

    PROCESS (CLK, RST)
    BEGIN
        IF (RST = '1') THEN
            Q_tmp <= '0';
        ELSIF (RISING_EDGE(CLK)) THEN
            Q_tmp <= D;
        END IF;
        Q <= Q_tmp;
        QN <= NOT Q_tmp;
    END PROCESS;
    
END Behavioral;

I implemented a PIPO register because it wasn't required to code a shift one, so it wasn't the wrong decision.

REGISTER_32.vhd

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY REGISTER_32 IS
    GENERIC (N: INTEGER := 32); -- Data width
    PORT (
        CLK : IN  STD_LOGIC;                      -- Clock input
        EN  : IN  STD_LOGIC;                      -- Enable input
        RST : IN  STD_LOGIC;                      -- Reset input (active-high)
        D   : IN  STD_LOGIC_VECTOR(N-1 DOWNTO 0); -- Data input
        Q   : OUT STD_LOGIC_VECTOR(N-1 DOWNTO 0); -- Data output
        QN  : OUT STD_LOGIC_VECTOR(N-1 DOWNTO 0)  -- Complement of the Data output
    );
END REGISTER_32;

ARCHITECTURE Behavioral OF REGISTER_32 IS

    SIGNAL Q_tmp:  STD_LOGIC_VECTOR(N-1 DOWNTO 0); -- Internal signal to store the output
    SIGNAL QN_tmp: STD_LOGIC_VECTOR(N-1 DOWNTO 0); -- Internal signal to store the complement of the output
    
BEGIN
    
    CELLS : FOR i IN 0 TO (N-1) GENERATE
        CELL_i : ENTITY work.DFF
            PORT MAP (
                CLK => CLK,
                RST => RST,
                D   => D(i),
                Q   => Q_tmp(i),
                QN  => QN_tmp(i)
            );
    END GENERATE CELLS;

    PROCESS (CLK)
    BEGIN
        IF (RISING_EDGE(CLK) AND EN = '1') THEN
            Q  <= Q_tmp;
            QN <= QN_tmp;
        END IF;
    END PROCESS;

END Behavioral;

RAM_32x32.vhd

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY RAM_32x32 IS
    GENERIC (N: INTEGER := 32); -- Data width and RAM depth
    PORT (
        CLK:   IN  STD_LOGIC;                       -- Clock
        WE:    IN  STD_LOGIC;                       -- Write Enable (Active-high)
        RST:   IN  STD_LOGIC;                       -- Reset (Active-high)
        ADDR:  IN  STD_LOGIC_VECTOR(4 DOWNTO 0);    -- Address
        DIN:   IN  STD_LOGIC_VECTOR(N-1 DOWNTO 0);
        DOUT:  OUT STD_LOGIC_VECTOR(N-1 DOWNTO 0);
        DOUTN: OUT STD_LOGIC_VECTOR(N-1 DOWNTO 0)
    );
END RAM_32x32;

ARCHITECTURE Behavioral OF RAM_32x32 IS

    TYPE RAM_ARRAY IS ARRAY (0 TO N-1) OF STD_LOGIC_VECTOR(N-1 DOWNTO 0);
    SIGNAL RAM_W  : RAM_ARRAY := (OTHERS => (OTHERS => '0'));               -- Write D Matrix
    SIGNAL RAM_R  : RAM_ARRAY;                                              -- Read Q Matrix
    SIGNAL RAM_RN : RAM_ARRAY;                                              -- Read QN Matrix
    
BEGIN
    
    REGISTERS : FOR i IN 0 TO (N-1) GENERATE
        REGISTER_i : ENTITY work.REGISTER_32
            PORT MAP (
                CLK => CLK,
                EN  => '1',
                RST => RST,
                D   => RAM_W(i),
                Q   => RAM_R(i),
                QN  => RAM_RN(i)
            );
    END GENERATE REGISTERS;
    
    DOUT <= RAM_R(TO_INTEGER(UNSIGNED(ADDR)));
    DOUTN <= RAM_RN(TO_INTEGER(UNSIGNED(ADDR)));
    
    PROCESS (CLK)
    BEGIN
        IF (RISING_EDGE(CLK) AND WE = '1') THEN
            RAM_W(TO_INTEGER(UNSIGNED(ADDR))) <= DIN;
        END IF;
    END PROCESS;

END Behavioral;

I still don't know how to manage the enable inputs of the registers.

Update

This is the code for the RAM_32x32.vhd file with the additions and modifications @TonyM advised.

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

entity ram_32x32 is
    port (
        clk  : in  std_logic;                     -- clock input
        rst  : in  std_logic;                     -- reset input (active-high)
        we   : in  std_logic;                     -- write enable input (active-high)
        addr : in  std_logic_vector(4 downto 0);  -- address input
        d    : in  std_logic_vector(31 downto 0); -- data input
        q    : out std_logic_vector(31 downto 0); -- data output
        q_n  : out std_logic_vector(31 downto 0)  -- complement of the data output
    );
end ram_32x32;

architecture structural of ram_32x32 is

    type memory is array (0 to 31) of std_logic_vector(31 downto 0);
    
    signal write_en    : std_logic_vector(0 to 31) := (others => '0');
    signal data_read   : memory;                                -- matrix of output data that was read
    signal data_read_n : memory;                                -- matrix of complemented output data that was read
    
begin
    
    paddr : process(we, addr)
    begin
        write_en <= (others => '0');
        if (we = '1') then
            write_en(to_integer(unsigned(addr))) <= '1';
        end if;
    end process paddr;
    
    ram : for i in 0 to 31 generate
        word_line : entity work.register_32
            port map (
                clk => clk,
                rst => rst,
                we  => write_en(i),
                d   => d,
                q   => data_read(i),
                q_n => data_read_n(i)
            );
    end generate ram;
    
    process(clk, rst)
    begin
        if (rst = '1') then                                 -- if the reset is active
            q   <= (others => '0');                         
            q_n <= (others => '1');                         
        elsif (rising_edge(clk)) then                       -- else, if a rising edge is detected on the clock
            q   <= data_read(to_integer(unsigned(addr)));   --     assign the read data from the specified address of the ram to the output
            q_n <= data_read_n(to_integer(unsigned(addr))); --     assign the read data from the specified address of the ram to the complemented output
        end if;
    end process;

end architecture structural;

Testbench code RAM_tb.vhd

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY RAM_tb IS
END RAM_tb;

ARCHITECTURE behavior OF RAM_tb IS

    COMPONENT RAM_32x32
        PORT(
            CLK  : IN  STD_LOGIC;
            RST  : IN  STD_LOGIC;
            WE   : IN  STD_LOGIC;
            ADDR : IN  STD_LOGIC_VECTOR(4 DOWNTO 0);
            D    : IN  STD_LOGIC_VECTOR(31 DOWNTO 0);
            Q    : OUT  STD_LOGIC_VECTOR(31 DOWNTO 0);
            Q_N  : OUT  STD_LOGIC_VECTOR(31 DOWNTO 0)
        );
    END COMPONENT;
    
    -- Inputs
    SIGNAL CLK  : STD_LOGIC := '0';
    SIGNAL RST  : STD_LOGIC := '0';
    SIGNAL WE   : STD_LOGIC := '0';
    SIGNAL ADDR : STD_LOGIC_VECTOR(4 DOWNTO 0) := (OTHERS => '0');
    SIGNAL D    : STD_LOGIC_VECTOR(31 DOWNTO 0) := (OTHERS => '0');
    
    -- Outputs
    SIGNAL Q   : STD_LOGIC_VECTOR(31 DOWNTO 0);
    SIGNAL Q_N : STD_LOGIC_VECTOR(31 DOWNTO 0);
    
    -- Clock period
    CONSTANT clock_period : TIME := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
    UUT: RAM_32x32
        PORT MAP (
            CLK  => CLK,
            RST  => RST,
            WE   => WE,
            ADDR => ADDR,
            D    => D,
            Q    => Q,
            Q_N  => Q_N
        );
    
    -- Clock process definitions
    pCLK : PROCESS
    BEGIN
        CLK <= '0';
        WAIT FOR clock_period/2;
        CLK <= '1';
        WAIT FOR clock_period/2;
    END PROCESS pCLK;

   -- Stimulus process
   pSTIM : PROCESS
   BEGIN        
        -- Reset RAM
        RST  <= '1';
        WAIT FOR 2*clock_period;
        RST  <= '0';
        WAIT FOR clock_period;

        -- Write data to RAM
        WE   <= '1';
        ADDR <= "00001";
        D    <= x"F0F0F0F0";
        WAIT FOR clock_period;
        WE   <= '0';
        WAIT FOR clock_period;

        -- Read data from RAM
        ADDR <= "00001";
        WAIT FOR clock_period;
        ASSERT (Q   = x"F0F0F0F0") REPORT "Error: Data Q read from address 00001 does not match data written" SEVERITY ERROR;
        ASSERT (Q_N = x"0F0F0F0F") REPORT "Error: Data Q_N read from address 00001 does not match data written" SEVERITY ERROR;
        
        -- Write data to another address
        WE   <= '1';
        ADDR <= "00010";
        D    <= x"AAAAAAAA";
        WAIT FOR clock_period;
        WE   <= '0';
        WAIT FOR clock_period;
        
        -- Read data from another address
        ADDR <= "00010";
        WAIT FOR clock_period;
        ASSERT (Q   = x"AAAAAAAA") REPORT "Error: Data Q read from address 00010 does not match data written" SEVERITY ERROR;
        ASSERT (Q_N = x"55555555") REPORT "Error: Data Q_N read from address 00010 does not match data written" SEVERITY ERROR;

        WAIT;
   END PROCESS pSTIM;

END ARCHITECTURE behavior;

Simulation graph

Testbench simulation result

\$\endgroup\$
2
  • \$\begingroup\$ A very warm welcome to the site. Is this a homework question? If so, have you been told to write this as three separate entities? Otherwise, it's a very long-winded way of writing it in VHDL, it'd normally be written much shorter. If you want a 32x DFF, you'd just use a std_logic_vector(31 downto 0) controlled by a single clocked process to implement it. \$\endgroup\$
    – TonyM
    Feb 19, 2023 at 18:42
  • \$\begingroup\$ I can guide you but not without the actual lab' question - can you edit your question and post that. I'm not sure if you should be making a RAM out of DFFs. In general, that depends on the technology and the RAM size. But first, the actual question in full please. \$\endgroup\$
    – TonyM
    Feb 20, 2023 at 9:30

1 Answer 1

2
\$\begingroup\$

You're most of the way there.

dff.vhd should be:

library ieee;
use ieee.std_logic_1164.all;


entity DFF is
  port(
    CLK                           : in  std_logic;   -- clock input
    RST                           : in  std_logic;   -- reset input, active high
    D                             : in  std_logic;   -- data input
    LE                            : in  std_logic;   -- latch enable
    Q                             : out std_logic;   -- data output
    QN                            : out std_logic    -- complement of data output
  );
end dff;


architecture BEHAVIORAL of DFF is
begin


  pFlipFlop : process(CLK, RST) is
  begin
    if (RST = '1') then
      Q   <=  '0';
      QN  <=  '1';

    elsif rising_edge(CLK) then

      if (LE = '1') then
        Q   <=      D;
        QN  <=  not D;
      end if;

    end if;
  end process pFlipFlop;
    
    
end architecture BEHAVIORAL;

register32.vhd is better named as dff32.vhd for continuity and should be what you have but modified to use the below. It also doesn't need the process or the signals. And it's not variable width so the generic can go.

  DFFsForSLV : for i in 31 downto 0 generate

    U_CELL : entity work.DFF
      port map(
        CLK                       =>  CLK,
        RST                       =>  RST,
        D                         =>  D(i),
        LE                        =>  LE,
        Q                         =>  Q(i),
        QN                        =>  QN(i)
      );

  end generate DFFsForSLV;

ram_32x32.vhd is different to what you had and simpler. I've left some parts missing so you can get them right yourself.

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


entity RAM_32X32 is
  port(
    CLK                           : in  std_logic;                        -- clock
    RST                           : in  std_logic;                        -- reset
    ADDR                          : in  std_logic_vector( 4 downto  0);   -- address
    WR_DATA                       : in  std_logic_vector(31 downto  0);
    WR_EN                         : in  std_logic;                        -- write enable
    RD_DATA                       : out std_logic_vector(31 downto  0);
    RD_DATA_N                     : out std_logic_vector(31 downto  0)
  );
end entity RAM_32X32;


architecture BEHAVIORAL of RAM_32X32 is

  signal wrEns                    : std_logic_vector(0 to 31);

  type typeRAMArray is array(0 to 31) of std_logic_vector(31 downto  0);
  signal rdDWords                 : typeRAMArray;
  signal rdDWordsN                : typeRAMArray;

begin


  wrEns  <=  X"00000000"  when (WR_EN = '0') else  ***1-HOT_FROM ADDR***


  MakeRAM : for i in 0 to (n-1) generate

    U_ROW : entity work.DFF32
      port map(
        CLK                       =>  clk,
        RST                       =>  rst,
        D                         =>  WR_DATA,
        LE                        =>  wrEns(i),
        Q                         =>  rdDWords(i),
        QN                        =>  rdDWordsN(i)
      );

  end generate MakeRAM;


  pReadRAM : process(RST, CLK) is
  begin
    if (RST = '1') then
      RD_DATA    <=  X"00000000";
      RD_DATA_N  <=  X"FFFFFFFF";

    elsif rising_edge(CLK) then
      RD_DATA    <=  rdDWords (to_integer(unsigned(ADDR));   ***CHECK_THIS***
      RD_DATA_N  <=  rdDWordsN(to_integer(unsigned(ADDR));   ***CHECK_THIS***

     end if;

  end process pReadRAM;


end architecture BEHAVIORAL;

Form a consistent style you can stick to and also that looks commonplace where commonplace is agreeable to you. Using 2-space TABs saves screen width. Using lower case for keywords is less shouty.

EDIT: after OP's revised design...

Try this. You may have to resolve some errors.

  pAddrDecoder : process(we, addr) is
  begin
    wrEns  <=  X"00000000";

    if (we = '1') then
      wrEns(to_integer(unsigned(addr)))  <=  '1';
    end if;

  end process pAddrDecoder;
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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