1

I'm trying to write a component (mem_interface) that takes 8 bit input vectors for address and data, writes them into specific positions in larger vector buffers depending on an index, and then outputs these larger vector buffers when asked. The component will sit in between a picoblaze processor (which can only output 8 bit values) and an Artix 7 BRAM, which I have configured with an 18 bit read/write port width (so 11 bit addresses). As an end goal, the mem_interface will accumulate multiple 8 bit outputs from the picoblaze and then read/write the BRAM at the provided address with the provided data.

The issue that I'm having is that writes to the mem_interface's internal data buffer do not take effect.

simulation screenshot.

At 15ns, the testbench first tests writing to the component's address buffer, which works perfectly. At 45ns, it attempts to write to the data buffer which immediately sends the targeted elements to 'X'. I've always understood 'X' to mean a multiple driver issue, but I can't identify one in my code.

Here's the code, mem_interface.vhdl first:

library ieee;
use ieee.std_logic_1164.all;

entity mem_interface is
    port (
            -- data inputs
            clk            : in std_logic;
            idx            : in std_logic_vector(1 downto 0);

            split_addr_in  : in std_logic_vector(7 downto 0);
            split_data_in  : in std_logic_vector(7 downto 0);
            parity_in      : in std_logic;

            split_data_out : out std_logic_vector(7 downto 0);
            parity_out     : out std_logic;

            -- signals
            addr_buf_en    : in std_logic;
            data_buf_wr    : in std_logic;
            data_buf_rd    : in std_logic;
            reset_bufs     : in std_logic;

            write_to_mem   : in std_logic;
            read_from_mem  : in std_logic;

            -- to bram
            addr_out       : out std_logic_vector(15 downto 0);
            data_out       : out std_logic_vector(35 downto 0);
            data_in        : in std_logic_vector(35 downto 0)
            );
end mem_interface;

architecture Behavioral of mem_interface is

    signal addr_buf : std_logic_vector(15 downto 0) := (others => '0');
    signal data_buf : std_logic_vector(35 downto 0) := (others => '0');

begin
    handle_addr_buf : process(clk)
    begin
        if rising_edge(clk) then
            if addr_buf_en = '1' then
                case idx(0) is
                    when '0' => addr_buf(7 downto 0) <= split_addr_in;

                    when '1' => addr_buf(15 downto 8) <= split_addr_in;   

                    when others => addr_buf <= "1010101010101010";       
                end case;
            elsif reset_bufs = '1' then
                addr_buf <= (others => '0');
            end if;
        end if;
    end process handle_addr_buf;

    handle_data_buf : process(clk)
    begin
        if rising_edge(clk) then
            if data_buf_wr = '1' then
                case idx is
                    when "00" => data_buf(7 downto 0) <= split_data_in;
                                 data_buf(32) <= parity_in;

                    when "01" => data_buf(15 downto 8) <= split_data_in;
                                 data_buf(33) <= parity_in;

                    when "10" => data_buf(23 downto 16) <= split_data_in;
                                 data_buf(34) <= parity_in;

                    when "11" => data_buf(31 downto 24) <= split_data_in;
                                 data_buf(35) <= parity_in;

                    when others => data_buf <= "101010101010101010101010101010101010";
                end case;
            elsif data_buf_rd = '1' then
                case idx is
                    when "00" => parity_out <= data_buf(32);
                                 split_data_out <= data_buf(7 downto 0);

                    when "01" => parity_out <= data_buf(33);
                                 split_data_out <= data_buf(15 downto 8);

                    when "10" => parity_out <= data_buf(34);
                                 split_data_out <= data_buf(23 downto 16);

                    when "11" => parity_out <= data_buf(35);
                                 split_data_out <= data_buf(31 downto 24);

                    when others => split_data_out <= "10101010";
                end case;
            elsif reset_bufs = '1' then
                data_buf <= (others => '0');
            end if;
        end if;
    end process handle_data_buf;

    handle_memory : process(clk)
    begin
        if rising_edge(clk) then
            if write_to_mem = '1' then
                addr_out <= addr_buf;
                data_out <= data_buf;
            elsif read_from_mem = '1' then
                addr_out <= addr_buf;
                data_buf <= data_in;
            else
                addr_out <= (others => '0');
                data_out <= (others => '0');
            end if;
        end if;
    end process handle_memory;
end Behavioral;

And here's the testbench, mem_interface_tb.vhdl

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

entity mem_interface_tb is
--  Port ( );
end mem_interface_tb;

architecture Behavioral of mem_interface_tb is
    component mem_interface is
        port (
            -- data inputs
            clk            : in std_logic;
            idx            : in std_logic_vector(1 downto 0);

            split_addr_in  : in std_logic_vector(7 downto 0);
            split_data_in  : in std_logic_vector(7 downto 0);
            parity_in      : in std_logic;

            split_data_out : out std_logic_vector(7 downto 0);
            parity_out     : out std_logic;

            -- signals
            addr_buf_en    : in std_logic;
            data_buf_wr    : in std_logic;
            data_buf_rd    : in std_logic;
            reset_bufs     : in std_logic;

            write_to_mem   : in std_logic;
            read_from_mem  : in std_logic;

            -- to bram
            addr_out       : out std_logic_vector(15 downto 0);
            data_out       : out std_logic_vector(35 downto 0);
            data_in        : in std_logic_vector(35 downto 0)
            );
    end component;

    component sim_clk_wrapper
        port (
                sim_clk_out : out std_logic;
                sim_rst_out : out std_logic
            );
    end component;

    signal sim_clk_sig : std_logic;
    signal sim_rst_sig : std_logic;

    signal sim_idx            : std_logic_vector(1 downto 0)  := "00";

    signal sim_split_addr_in  : std_logic_vector(7 downto 0)  := (others => '0');
    signal sim_split_data_in  : std_logic_vector(7 downto 0)  := (others => '0');
    signal sim_parity_in      : std_logic                     := '0';

    signal sim_split_data_out : std_logic_vector(7 downto 0)  := (others => '0');
    signal sim_parity_out     : std_logic                     := '0';

    signal sim_addr_buf_en    : std_logic                     := '0';
    signal sim_data_buf_wr    : std_logic                     := '0';
    signal sim_data_buf_rd    : std_logic                     := '0';
    signal sim_reset_bufs     : std_logic                     := '0';

    signal sim_write_to_mem   : std_logic                     := '0';
    signal sim_read_from_mem  : std_logic                     := '0';

    signal sim_addr_out       : std_logic_vector(15 downto 0) := (others => '0');
    signal sim_data_out       : std_logic_vector(35 downto 0) := (others => '0');
    signal sim_data_in        : std_logic_vector(35 downto 0) := (others => '0');

    type bram_36k is array (0 to 2048) of std_logic_vector(17 downto 0);
    signal sim_bram_36k : bram_36k;
begin
    sim_clk_gen : sim_clk_wrapper
        port map (
                    sim_clk_out => sim_clk_sig,
                    sim_rst_out => sim_rst_sig
                 );

    UUT : mem_interface
        port map (
                    clk            => sim_clk_sig,
                    idx            => sim_idx,

                    split_addr_in  => sim_split_addr_in,
                    split_data_in  => sim_split_data_in,
                    parity_in      => sim_parity_in,

                    split_data_out => sim_split_data_out,
                    parity_out     => sim_parity_out,

                    addr_buf_en    => sim_addr_buf_en,
                    data_buf_wr    => sim_data_buf_wr,
                    data_buf_rd    => sim_data_buf_rd,
                    reset_bufs     => sim_reset_bufs,

                    write_to_mem   => sim_write_to_mem,
                    read_from_mem  => sim_read_from_mem,

                    addr_out       => sim_addr_out,
                    data_out       => sim_data_out,
                    data_in        => sim_data_in
                );

    -- simulating 18 bit port sizes with 11 bit addresses
    manage_sim_bram : process(sim_clk_sig)
    begin
        if rising_edge(sim_clk_sig) then
            if sim_write_to_mem = '1' then
                sim_bram_36k(to_integer(unsigned(sim_addr_out(14 downto 4)))) <= sim_data_out(33 downto 32) & sim_data_out(15 downto 0);
            elsif sim_read_from_mem = '1' then
                sim_data_in(33 downto 32) <= sim_bram_36k(to_integer(unsigned(sim_addr_out(14 downto 4))))(17 downto 16);
                sim_data_in(15 downto 0) <= sim_bram_36k(to_integer(unsigned(sim_addr_out(14 downto 4))))(15 downto 0);
            end if;
        end if;
    end process manage_sim_bram;

    simulate: process
    begin
        wait until rising_edge(sim_clk_sig);
        sim_reset_bufs <= '1';

        wait until rising_edge(sim_clk_sig);
        -- first write an address to the buffer

        sim_reset_bufs <= '0';
        sim_addr_buf_en <= '1';
        sim_idx <= "00";
        sim_split_addr_in <= "00001111";

        wait until rising_edge(sim_clk_sig);

        sim_idx <= "01";
        sim_split_addr_in <= "10000000";

        wait until rising_edge(sim_clk_sig);
        -- disable writing to address

        sim_addr_buf_en <= '0';
        sim_split_addr_in <= "00000000";
        sim_idx <= "00";

        wait until rising_edge(sim_clk_sig);
        -- write to the data

        sim_data_buf_wr <= '1';
        sim_idx <= "00";

        sim_split_data_in <= "11111111";
        sim_parity_in <= '1';

        wait until rising_edge(sim_clk_sig);

        sim_idx <= "01";
        sim_split_data_in <= "11111111";
        sim_parity_in <= '1';

        wait until rising_edge(sim_clk_sig);

        -- then write the data to the memory
        sim_data_buf_wr <= '0';
        sim_split_data_in <= "00000000";
        sim_parity_in <= '0';

        sim_write_to_mem <= '1';

        wait until rising_edge(sim_clk_sig);
        wait until rising_edge(sim_clk_sig);
        wait until rising_edge(sim_clk_sig);
    end process simulate;

end Behavioral;

Any help would be appreciated, I've been staring at this for a few hours and have no idea where the issue is.

Paebbels
  • 15,573
  • 13
  • 70
  • 139
Deskarano
  • 33
  • 1
  • 3
  • While you're not providing a [mcve], the good news is the (this) problem is indeed multiple drivers and that can be found searching mem_interface.vhdl. You're assigning data_buf in two processes (handle_data_buf, handle_memory). –  Mar 04 '19 at 00:43
  • I see that multiple driver issue. I thought that this would be okay since I am never raising both data_buf_wr and read_from_mem at the same time, but I can see how this could lead to ambiguous behavior. I moved the `data_buf <= data_in` statement to an elsif block in the `handle_data_buf` process and this solved the issue. Is it typically best practice to make sure that signals are only touched by one process? – Deskarano Mar 04 '19 at 01:51
  • @Deskarano Yes. Think of a process as _a little bit of software that models a little bit of hardware_. If you have two processes driving a signal then that is the same as having two bits of hardware driving that signal. Unless you're using 3-state logic (which you shouldn't be inside an IC) then this is contention. – Matthew Taylor Mar 04 '19 at 09:37
  • That makes a lot of sense! Thanks for your help. – Deskarano Mar 04 '19 at 15:16

0 Answers0