1
\$\begingroup\$

Question moved to: https://codereview.stackexchange.com/questions/135868/vhdl-pipeline-with-a-for-loop

I'm implementing an AXI4-Stream module. The module uses three DSP blocks (DSP49E1, UG479 - Xilinx). In order to run the module at a frequency of 150 MHz, the design is a pipeline going successively through each DSP.

In the code below, I made the choice of a for loop into a single process to implement the pipeline. I should say that I have simulated and tested this design on a FPGA (7 series) and it works perfectly fine so far.

My questions are: Would that be better to create independent proccesses instead of a for loop inside a single process? I like the way I coded the pipeline because it saved me time (for the shift register) thanks to the for loop and the arrays of std_logic_vector. However, is it a good way of coding it (frequency, power, FPGA utilization,...)? More generally, is it a good design practice or is it limiting for certain purposes. I would like to understand the whys and wherefores of my choice.

Thank you.

My code:

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

entity slave_AXIStream_RGBtoGray is
port (
    -- Main signals
    CLK                 : in std_logic;
    RESETN              : in std_logic;

    -- Ready signal for upstream block
    S_AXIS_TREADY       : out std_logic;
    -- Data in
    S_AXIS_TDATA        : in std_logic_vector(23 downto 0);
    -- Flag for first pixel of a frame
    S_AXIS_TUSER        : in std_logic;
    -- Flag for last pixel of a line
    S_AXIS_TLAST        : in std_logic;
   -- Valid data
   S_AXIS_TVALID       : in std_logic;

    -- Downstream blocks are ready
    M_AXIS_TREADY       : in std_logic;    
    -- Data out
    M_AXIS_TDATA        : out std_logic_vector(7 downto 0);
    -- Flag for first pixel of a frame
    M_AXIS_TUSER        : out std_logic;
    -- Flag for last pixel of a line
    M_AXIS_TLAST        : out std_logic;
    -- Valid data
    M_AXIS_TVALID       : out std_logic    
);
end slave_AXIStream_RGBtoGray;

architecture Behavioral of slave_AXIStream_RGBtoGray is

-- DSP to perform A*B+C 
COMPONENT dsp48E1_macro
    PORT (
        CLK : IN STD_LOGIC;
        CE : IN STD_LOGIC;
        SCLR : IN STD_LOGIC;
        A : IN STD_LOGIC_VECTOR(14 DOWNTO 0); -- signed, two's complement
        B : IN STD_LOGIC_VECTOR(14 DOWNTO 0); -- signed, two's complement
        C : IN STD_LOGIC_VECTOR(29 DOWNTO 0); -- signed, two's complement
        P : OUT STD_LOGIC_VECTOR(30 DOWNTO 0) -- signed, two's complement
    );
END COMPONENT;

-- main signals
signal main_ready               : std_logic;

-- DSP48E1 signals
signal dsp_A_out                : std_logic_vector(30 downto 0);    
signal dsp_B_out                : std_logic_vector(30 downto 0);
signal dsp_C_out                : std_logic_vector(30 downto 0);

-- Out = coef_red * component_red + coef_green * component_green + coef_blue * component_blue
-- sum of the factors <= 1
constant coef_red               : std_logic_vector(14 downto 0) := to_slv(to_ufixed (0.29, 8, -6));
constant coef_green             : std_logic_vector(14 downto 0) := to_slv(to_ufixed (0.59, 8, -6));
constant coef_blue              : std_logic_vector(14 downto 0) := to_slv(to_ufixed (0.11, 8, -6));

-- Pipeline
signal pipeline_step            : integer range 0 to 13;       
type array_component_2DSP is array(0 to 8) of std_logic_vector(7 downto 0);
type array_component_1DSP is array(0 to 4) of std_logic_vector(7 downto 0);
signal component_red_dsp        : std_logic_vector(7 downto 0);
signal component_green_dsp      : array_component_1DSP;
signal component_blue_dsp       : array_component_2DSP;
signal DV_step_dsp              : std_logic_vector(0 to 12); -- data valid
signal SOF_step_dsp             : std_logic_vector(0 to 12); -- start of frame
signal EOL_step_dsp             : std_logic_vector(0 to 12); -- end of line

-- Last step
signal component_mono_step_out  : std_logic_vector(7 downto 0);    
signal DV_step_out              : std_logic; -- data valid
signal SOF_step_out             : std_logic; -- start of frame
signal EOL_step_out             : std_logic; -- end of line

signal data_stored              : std_logic_vector(7 downto 0);
signal DV_stored                : std_logic; -- data valid
signal SOF_stored               : std_logic; -- start of frame
signal EOL_stored               : std_logic; -- end of line

begin

S_AXIS_TREADY <= main_ready;

M_AXIS_TDATA <= component_mono_step_out;
M_AXIS_TVALID <= DV_step_out;
M_AXIS_TUSER <= SOF_step_out;
M_AXIS_TLAST <= EOL_step_out;

DSP_A: dsp48E1_macro
PORT MAP (
    CLK => CLK,
    CE => main_ready,
    SCLR => not RESETN,
    A => '0' & component_red_dsp & "000000",
    B => coef_red,
    C => (others => '0'),
    P => dsp_A_out
);

DSP_B: dsp48E1_macro
PORT MAP (
    CLK => CLK,
    CE => main_ready,
    SCLR => not RESETN,
    A => '0' & component_green_dsp(4) & "000000",
    B => coef_green,
    C => dsp_A_out(29 downto 0),
    P => dsp_B_out
);

DSP_C: dsp48E1_macro
PORT MAP (
    CLK => CLK,
    CE => main_ready,
    SCLR => not RESETN,
    A => '0' & component_blue_dsp(8) & "000000",
    B => coef_blue,
    C => dsp_B_out(29 downto 0),
    P => dsp_C_out
);        

main_process: process(CLK)
begin
if (rising_edge (CLK)) then
if(RESETN = '0') then
    pipeline_step <= 0;
    main_ready <= '0';

    component_red_dsp <= (others => '0');
    component_green_dsp <= (others => (others => '0'));
    component_blue_dsp <= (others => (others => '0'));

    DV_step_dsp <= (others => '0');
    SOF_step_dsp <= (others => '0');
    EOL_step_dsp <= (others => '0');

    component_mono_step_out <= (others => '0');
    DV_step_out <= '0';
    SOF_step_out <= '0';
    EOL_step_out <= '0';

    DV_stored <= '0';
    SOF_stored <= '0';
    EOL_stored <= '0';
else

    FOR pipeline_step IN 0 TO 13 LOOP
        CASE pipeline_step IS
            WHEN 0 => 
                if main_ready = '1' then
                    component_red_dsp <= S_AXIS_TDATA(23 downto 16);
                    component_green_dsp(pipeline_step) <= S_AXIS_TDATA(15 downto 8);
                    component_blue_dsp(pipeline_step) <= S_AXIS_TDATA(7 downto 0);
                    DV_step_dsp(pipeline_step) <= S_AXIS_TVALID;
                    SOF_step_dsp(pipeline_step) <= S_AXIS_TUSER;
                    EOL_step_dsp(pipeline_step) <= S_AXIS_TLAST;
                end if;

            WHEN 1 to 4 => 
                if main_ready = '1' then
                    component_green_dsp(pipeline_step) <= component_green_dsp(pipeline_step-1);
                    component_blue_dsp(pipeline_step) <= component_blue_dsp(pipeline_step-1);
                    DV_step_dsp(pipeline_step) <= DV_step_dsp(pipeline_step-1); 
                    SOF_step_dsp(pipeline_step) <= SOF_step_dsp(pipeline_step-1);
                    EOL_step_dsp(pipeline_step) <= EOL_step_dsp(pipeline_step-1);
                end if;            

            WHEN 5 to 8 => 
                if main_ready = '1' then
                    component_blue_dsp(pipeline_step) <= component_blue_dsp(pipeline_step-1);
                    DV_step_dsp(pipeline_step) <= DV_step_dsp(pipeline_step-1); 
                    SOF_step_dsp(pipeline_step) <= SOF_step_dsp(pipeline_step-1);
                    EOL_step_dsp(pipeline_step) <= EOL_step_dsp(pipeline_step-1);
                end if;

            WHEN 9 to 12 => 
                if main_ready = '1' then
                    DV_step_dsp(pipeline_step) <= DV_step_dsp(pipeline_step-1); 
                    SOF_step_dsp(pipeline_step) <= SOF_step_dsp(pipeline_step-1);
                    EOL_step_dsp(pipeline_step) <= EOL_step_dsp(pipeline_step-1); 
                end if;                     

            WHEN 13 =>
                if M_AXIS_TREADY = '1' or DV_step_out = '0' then
                    main_ready <= '1';
                    DV_step_out <= '0';

                    if main_ready = '1' then
                        component_mono_step_out <= dsp_C_out(19 downto 12); 
                        DV_step_out <= DV_step_dsp(pipeline_step-1);
                        SOF_step_out <= SOF_step_dsp(pipeline_step-1);
                        EOL_step_out <= EOL_step_dsp(pipeline_step-1); 
                    else
                        component_mono_step_out <= data_stored; 
                        DV_step_out <= DV_stored;
                        SOF_step_out <= SOF_stored;
                        EOL_step_out <= EOL_stored;  
                        DV_stored <= '0';
                    end if;
                else
                    main_ready <= '0';

                    if main_ready = '1' then
                        data_stored <= dsp_C_out(19 downto 12);
                        DV_stored <= DV_step_dsp(pipeline_step-1);
                        SOF_stored <= SOF_step_dsp(pipeline_step-1);
                        EOL_stored <= EOL_step_dsp(pipeline_step-1); 
                    end if;
                end if;

            WHEN others => NULL;
        END CASE;
    END LOOP;
end if;
end if;
end process;

end Behavioral;
\$\endgroup\$
  • \$\begingroup\$ This sound like a CodeReview question :). However, I prefer generate statements over complex processes. Process descriptions won't unveil pipeline wiring faults, because a process can't create multiple drivers. In a generate description, such a fault creates multiple driver which can be detected by the tools. \$\endgroup\$ – Paebbels Jul 25 '16 at 15:45
  • \$\begingroup\$ Thank you for the answer @Paebbels! I moved my question to [codereview.stackexchange.com/questions/135868/…. If I got it right, generate statements allow to create independent processes (I'm not very familiar with them). To do so, is it actually required to create 5 different entities that I could call in a for generate loop? I think I understand the advantages (detection of the wiring faults) but it seems a bit complicated compared to what I would like to do here, but maybe there is a much easier way to use generate statements... \$\endgroup\$ – Marmoz Jul 25 '16 at 16:19
  • \$\begingroup\$ OK, I just realized that I misunderstood how generate does actually work. I'm going to give it a try \$\endgroup\$ – Marmoz Jul 25 '16 at 16:37
  • \$\begingroup\$ Here are two generate example, which I wrote: 1) an Odd-Even Sort sorting network. It has a generate loop for the stages and a simple odd/even decision to instantiate the correct stage element. 2) is an Odd-Even Mergesort sorting network. It has 5 nested generate loops. \$\endgroup\$ – Paebbels Jul 25 '16 at 16:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.