3

I am trying to learn good vhdl design methodologies. I was trying to find some advises about multiple signal drivers. Unfortunatelly without success. My question is if it is good idea to have signal with multiple drives (or I shall avoid it almost always).

My problem: - I have some transmission module. I would like to have some indicator (just for example only one) informing that there is a new data. One process set the flag if new data is available, other one clear flag after read.

In this case I have two process which drive one resolved signal. The problem which I can see here is concurency. Does exist good development pattern for problem like this ? Shall I avoid and never use multiple signal drivers ?

e2p
  • 163
  • 3
  • 12
  • 4
    In general, never use multiple drivers. One way you can implement this is to use an OR gate for all your driving signals. If any of the signals is active, the output goes active. If you need to reset, you will need some further logic to reset you signals – Adam Z Jul 10 '17 at 13:06
  • 1
    To be correct: There exist no multiple drivers in VHDL. It's actually about multiple sources :). – Paebbels Jul 25 '17 at 06:16

2 Answers2

2

You can't have multiple drivers for any given signal

From your description, it sounds like you are trying to work out some sort of handshaking between your two processes. While there may be a more elegant solution, without having seen your code, let's assume you actually do need handshaking. Even if you don't need it now, you may later.

To assist the discussion, I'll model the behavior using two components (or entities) instead of processes, so the diagram is more clear.

Assume you have one entity which receives data and another which processes the data. Also assume you only want to receive new data while the processor is idle (ie. it has finished working on the last data set).

For this, you'll need some handshaking. The first entity (call it data_memory) needs to know when the data_processor is idle, and the data_processor needs to know when the memory has data_rdy.

This resembles your post:

I would like to have some indicator (just for example only one) informing that there is a new data. One process set the flag if new data is available, other one clear flag after read. [sic]

Here we have a flag data_rdy which is set when new data is available, and cleared after it is read and processed:

RTL Diagram

For the data_memory instance, we can just clock the data directly from data_in to the data_processor whenever the write enable wren is high. The data_rdy flag lets the processor know when new data is ready and waiting.

-- data_memory.vhd
library ieee;
use ieee.std_logic_1164.all;

entity data_memory is
    port ( data_in  : in  std_logic_vector (7 downto 0);
           wren     : in  std_logic;
           rdy_read : in  std_logic;
           clk      : in  std_logic;
           data_out : out std_logic_vector (7 downto 0);
           data_rdy : out std_logic );
end data_memory;

architecture Behavioral of data_memory is
begin
    process (clk)
    begin
        if (rising_edge(clk)) then
            if (wren = '1') then
                -- in practice, we would add a queue here
                data_out <= data_in;
            end if;
            data_rdy <= wren and rdy_read;
        end if;
    end process;
end Behavioral;

The data_processor then uses the data whenever the data_rdy signal is high:

-- data_processor.vhd
library ieee;
use ieee.std_logic_1164.all;

entity data_processor is
    port ( data_in  : in  std_logic_vector (7 downto 0);
           data_rdy : in  std_logic;
           clk      : in  std_logic;
           data_out : out std_logic_vector (7 downto 0);
           idle     : out std_logic );
end data_processor;

architecture Behavioral of data_processor is
    signal process_wait_cycle  : std_logic_vector (63 downto 0) := (others => '1');
begin
    process (clk)
    begin
        if (rising_edge(clk)) then
            if (data_rdy = '1') then
                -- in practice, we would actually do something with the data;
                -- here we'll just make a copy and send it out
                data_out <= data_in;
                process_wait_cycle <= (others => '0');
                idle <= '0';
            else
                process_wait_cycle <= process_wait_cycle(62 downto 0) & '1';
                idle <= process_wait_cycle(63);
            end if;
        end if;
    end process;
end Behavioral;

The process_wait_cycle shift register is used here only as a means to eat up some clock cycles to simulate a processing time. When the processing is complete, the entity sets the idle flag high, which then loops back to the input of the data_memory block.

Finally, we can tie these two together in a top-level instance:

-- transmission.vhd
library ieee;
use ieee.std_logic_1164.all;

entity transmission is
    port ( data_in     : in  std_logic_vector (7 downto 0);
           wren        : in  std_logic;
           clk         : in  std_logic;
           data_out    : out std_logic_vector (7 downto 0);
           data_status : out std_logic );
end transmission;

architecture Behavioral of transmission is

-- Component data_memory
component data_memory is
port (
    data_in  : in  std_logic_vector (7 downto 0);
    wren     : in  std_logic;
    rdy_read : in  std_logic;
    clk      : in  std_logic;
    data_out : out std_logic_vector (7 downto 0);
    data_rdy : out std_logic );
end component;

-- Component data_processor
component data_processor is
port (
    data_in  : in  std_logic_vector (7 downto 0);
    data_rdy : in  std_logic;
    clk      : in  std_logic;
    data_out : out std_logic_vector (7 downto 0);
    idle     : out std_logic );
end component;

-- Signals
signal data_rdy        : std_logic := '0';
signal processor_ready : std_logic := '0';
signal data_from_mem   : std_logic_vector (7 downto 0) := X"00";

begin

    data_status <= data_rdy;

    -- data_memory instance
    data_memory_inst : data_memory
    port map (
        data_in  => data_in,
        wren     => wren,
        rdy_read => processor_ready,
        clk      => clk,
        data_out => data_from_mem,
        data_rdy => data_rdy );

    -- data_processor instance
    data_processor_inst : data_processor
    port map (
        data_in  => data_from_mem,
        data_rdy => data_rdy,
        clk      => clk,
        data_out => data_out,
        idle     => processor_ready );

end Behavioral;

And simulate: simulation

Here you can see that data is only used when wren and idle is high, indicating that both the processor is idle, and new data is incoming.

In case you're interested in the simulation file:

-- Transmission sim
library ieee;
use ieee.std_logic_1164.all;
use ieee.math_real.all;
use ieee.numeric_std.all;

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

architecture Behavioral of transmission_sim is

component transmission is
port ( data_in     : in  std_logic_vector (7 downto 0);
       wren        : in  std_logic;
       clk         : in  std_logic;
       data_out    : out std_logic_vector (7 downto 0);
       data_status : out std_logic );
end component;

signal data_in     : std_logic_vector (7 downto 0) := X"AB";
signal wren        : std_logic := '0';
signal clk         : std_logic := '0';
signal data_out    : std_logic_vector (7 downto 0) := X"00";
signal data_status : std_logic := '0';

constant PERIOD : time := 10 ns;
constant DUTY   : real := 0.5;

begin

    uut : transmission
    port map (
        data_in     => data_in,
        wren        => wren,
        clk         => clk,
        data_out    => data_out,
        data_status => data_status );

    process
    begin
        loop
            clk <= '0';
            wait for (PERIOD - (PERIOD * DUTY));
            clk <= '1';
            wait for (PERIOD - (PERIOD * DUTY));
        end loop;
    end process;

    process
        variable seed1, seed2: positive;
        variable rand: real;
        variable range_of_rand : real := 255.0;
    begin
        wait for (PERIOD*10);
        wren <= '1';
        wait for (PERIOD*10);
        wren <= '0';
        wait for (PERIOD*10);
        uniform(seed1, seed2, rand);
        data_in <= std_logic_vector(to_unsigned(integer(rand*range_of_rand), data_in'length));
    end process;

end Behavioral;
Blair Fonville
  • 3,837
  • 4
  • 19
  • 45
  • Do you mean [BLUF](https://en.wikipedia.org/wiki/BLUF_(fetishism)) or [BLUF](https://en.wikipedia.org/wiki/BLUF_(communication))? I'm 99.9% certain that it's the latter, but I had to google for it. – Harry Svensson Oct 27 '17 at 03:31
  • @HarrySvensson Haha. Of course the second one. The first is a new one for me. We use BLUF (bottom line up front) in a lot of emails in my department. People have a tendency to want to get to the point immediately. – Blair Fonville Oct 27 '17 at 03:34
0

Yes, avoid multiple drivers.

You can have two signals: one that is high for a clock after new data, and one that is high for a clock after read. Then in a process you can do

if rising_edge(CLK) then 
    if write_sig = '1' then
        flag <= '1';
    elsif read_sig = '1' then
        flag <= '0';
    end if;
end if;

It is up to you to decide the precedence of setting or clearing the flag, but prioritizing setting the flag seems to make the most sense.

Ethan
  • 463
  • 1
  • 6
  • 15