Oct 14, 2018

Simple and efficient FPGA reset method (reset for records)

I hope you are using VHDL records extensively. They are not as powerful as SystemVerilog data types and interfaces, but it's a huge step forward from individual bits and bit arrays of Verilog. I don't understand why some people use VHDL and don't use this feature of VHDL! Records make your code significantly clearer and crisper. They reduce risk of forgetting to assign a value to a signal of a complex data bus and make your description 10 times shorter while improving readability.



Let's assume that you are using AXI-Stream and you have defined

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   type AxiStreamType is record
      Data : std_logic_vector(63 downto 0);
      Last : std_logic;
      Valid : std_logic;
      Good : std_logic;
      Keep : std_logic_vector(7 downto 0);
   end record;

   signal Data1 : AxiStreamType;
   signal Data2 : AxiStreamType;

Then you can assign signals without having to assign each field individually.


1
2
3
4
5
6
7
8
9
   NoRstProc : process (clk) is
   begin
      if rising_edge(clk) then
         Data2 <= Data1;
         if SomeCodition then
            Data2.Last <= '1';
         end if;
      end if;
   end process;

Try to repeat the same without records and fill the difference.

But how do you reset such a signal? Of course you use the right reset style and reset only signals, you need to reset, but how exactly? You can do it like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
   SimpleProc : process (clk) is
   begin
      if rising_edge(clk) then
         Data2 <= Data1;
         if SomeCodition then
            Data2.Last <= '1';
         end if;
         
         if rst = '1' then
            Data2.Valid <= '0';
         end if;
      end if;
   end process;

but it doesn't scale well. For example, it becomes inconvenient to reset complex data types like records, containing an array of records or even something like this


1
2
   type AxiStreamArrayType is array(0 to 7)
                        of AxiStreamType;

There is a neat trick I use. Each time I define a record, I also define a reset value for this type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
   type AxiStreamType is record
      Data : std_logic_vector(63 downto 0);
      Last : std_logic;
      Valid : std_logic;
      Good : std_logic;
      Keep : std_logic_vector(7 downto 0);
   end record;
   
   constant InvalidAxiStream : AxiStreamType :=
   (
      Data => (others => 'U'),
      Last => 'U',
      Valid => '0',
      Good => 'U',
      Keep => (others => 'U')
 );
 
   type AxiStreamArrayType is array(0 to 7) of AxiStreamType;
   constant InvalidAxiStreamArray : AxiStreamArrayType :=
                              (others => InvalidAxiStream);

Having done this, you can reset your signals using this predefined constants.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
   NeatProc : process (clk) is
   begin
      if rising_edge(clk) then
         Data4 <= Data3;
         if SomeCodition then
            -- some other actions
         end if;
         
         if rst = '1' then
            Data4 <= InvalidAxiStreamArray;
         end if;
      end if;
   end process;

You can see how laconic it is. You can see that all fields in InvalidAxiStream have undefined values except for Valid field, which we really need to reset. We explicitly tell to synthesizer that we don't care about values of this signals during reset and the synthesizer doesn't generate any logic to reset these signals or to preserve their values during reset. It's exactly what we want. There will be neither reset generated for, say, Keep signal, nor CE logic for the FFs.

Unfortunately, this trick cannot be used for record fields of type integer or enumerated types, so I have to assign some real values to them and reset these fields even if I don't need to. In case I feel super frugal and I don't want to reset additional FFs, I reset only Valid field explicitly:


1
2
3
4
5
         if rst = '1' then
            for stream in Data4'range loop
               Data4(stream).Valid <= '0';
            end loop;
         end if;

This situation is an exception, however. In all other cases this coding style works great!

Please, tell me what you think! Thanks!

No comments:

Post a Comment