4.3.1. AES Example

As part of FOBOS, we provide a simple AES-128 implementation in VHDL. The VHDL code for this AES implementation is provided in the directory dut/example_cores/AES-128. The block diagram of this implementation is shown in Fig. 4.8 and the technical details in Table 4.3.

../_images/aes128.png

Fig. 4.8 Block Diagram of the Example AES Implementation

Table 4.3 Technical Details of the Provided Example AES-128

Interface

AXI

PDI width

128

SDI width

128

DO width

128

Rounds per clock cycle

1

Clock cycles for encrypting one block

11

4.3.1.1. First Steps

Create a new directory for the AES DUT project for example AES-DUT. Copy all files of the example AES from the dut/example_cores/AES-128/vhdl directory, all files from the dut/fpga_wrapper/src_rtl directory and the dut/fpga_wrapper/src_tb/core_wrapper_tb.vhd to this directory. Additionally copy the constraint file that matches your DUT from the dut/fpga_wrapper/constraints directory. Only modify the copied files. The example below assumes that you have installed FOBOS in /opt and that your DUT is the FOBOS FBD-A7.

Listing 4.1 Command line example for copying all relevant files
mkdir AES-DUT
cd !$
cp /opt/fobos/dut/example_cores/AES-128/vhdl/* .
cp /opt/fobos/dut/fpga_wrapper/src_rtl/* .
cp /opt/fobos/dut/fpga_wrapper/src_tb/core_wrapper_tb.vhd .
cp /opt/fobos/dut/fpga_wrapper/constraints/FOBOS_Artix7.xdc .

4.3.1.2. AES Top Level Interface

The top level file of the example AES implementation is aes_axi.vhd. Its interface is shown in Listing 4.2. It features three AXI4-Stream compatible interfaces, one for plaintext (PDI), key (SDI), and ciphertext (DO) as well as a clock and reset input. This interface matches exactly the FOBOS wrapper’s interface for the Crypto Core. It is a wrapper for the basic AES implementation.

Listing 4.2 Entity declaration of the example AES AXI wrapper
entity aes_axi is
    Port ( clk       : in  STD_LOGIC;
           rst       : in  STD_LOGIC;
           pdi_data  : in  STD_LOGIC_VECTOR (127 downto 0);
           pdi_valid : in  STD_LOGIC;
           pdi_ready : out STD_LOGIC;
           sdi_data  : in  STD_LOGIC_VECTOR (127 downto 0);
           sdi_valid : in  STD_LOGIC;
           sdi_ready : out STD_LOGIC;
           do_data   : out STD_LOGIC_VECTOR (127 downto 0);
           do_valid  : out STD_LOGIC;
           do_ready  : in  STD_LOGIC
                      );
end aes_axi;

The file aes_axi translates the very simple interface of the basic AES implementation which uses out-of-band signaling. It is shown in Listing 4.3. It expects one block of plaintext or 128-bit, and one 128-bit key on the input and produces 128-bit ciphertext. It has a clock signal and is idle until start is applied. Once the output is ready, it asserts the done signal. As start and done are explicit signals and not part of the data_in, key_in, or data_out signals, we call then out-of-band. On the other hand, in-band signaling, as used by the LWC Hardware API (see Section 4.2.4) requires a comprehensive protocol of commands and parameters being sent and received via PDI, SDI, and DO.

Listing 4.3 Entity declaration of the basic AES with out-of-band signaling
entity aes_non_pipe is
port (
    clock    : in  std_logic ;
    start    : in  std_logic ;
    data_in  : in  std_logic_vector (0 to 127);
    key_in   : in  std_logic_vector (0 to 127);
    data_out : out std_logic_vector (0 to 127);
    done     : out std_logic
);

end aes_non_pipe;

It is therefore easily possible to adjust the file aes_axi to other block cipher implementations which use such simple out-of-band signaling.

Note

FOBOS is not concerned with what data is transmitted to PDI, SDI, or comes from DO, i.e., whether the Crypto Core requires in-band signaling (LWC Hardware API) or not. FOBOS only needs to know which data goes to which FIFO and how deep the FIFO has to be. FOBOS does not support out-of-band signaling. If that is required it has to be derived from the AXI protocol as in the example AES, or the FOBOS protocol has to be adjusted.

4.3.1.3. FOBOS Wrapper Configuration

The AES example has an AXI interface and has to be instantiated as follows in the core_wrapper.vhd file around line 173. These are the default settings for this file. Note that the reset signal has to be inverted.

Listing 4.4 Port Map for example AES in core_wrapper.vhd
--=============================================
-- BEGING USER CRYPTO
-- Instantiate your core here
crypto_core : entity work.aes_axi(behav)
port map(
    clk         => clk,
    rst         => not crypto_input_en,
    -- data signals
    pdi_data    => crypto_di0_data,
    pdi_valid   => crypto_di0_valid,
    pdi_ready   => crypto_di0_ready,

    sdi_data    => crypto_di1_data,
    sdi_valid   => crypto_di1_valid,
    sdi_ready   => crypto_di1_ready,

    do_data     => crypto_do_data,
    do_ready    => crypto_do_ready,
    do_valid    => crypto_do_valid

    --! if rdi_interface for side-channel protected versions is required, uncomment the rdi interface
    -- ,rdi_data => crypto_rdi_data,
    -- rdi_ready => crypto_rdi_ready,
    -- rdi_valid => crypto_rdi_valid
);
-- END USER CRYPTO
--=============================================

The example AES expects one block of plaintext or 128-bit, and one 128-bit key on the input. These have to be placed on FIFO_0 and FIFO_1 respectively. As the width of PDI and SDI are 128-bit, the width of these FIFOs have to match and they have to be only one word deep. The output of this AES is also 128-bit wide, hence FIFO_OUT also has to be 128-bit wide and one word deep. This has to be defined in core_wrapper_pkg.vhd which is shown in Listing 4.5. This is also the default configuration of this file.

Listing 4.5 FIFO definitions for example AES in core_wrapper_pkg.vhd
package core_wrapper_pkg is
    -- input fifos
    constant FIFO_0_WIDTH           : natural := 128    ;
    constant FIFO_0_LOG2DEPTH       : natural := 1      ;
    constant FIFO_1_WIDTH           : natural := 128    ;
    constant FIFO_1_LOG2DEPTH       : natural := 1      ;
    -- output fifo
    constant FIFO_OUT_WIDTH         : natural := 128    ;
    constant FIFO_OUT_LOG2DEPTH     : natural := 1      ;
    -- random data
    constant RAND_WORDS             : natural := 8      ;
    constant FIFO_RDI_WIDTH         : natural := 64     ;
    constant FIFO_RDI_LOG2DEPTH     : natural := 3      ;

end core_wrapper_pkg;

Note

The depth of a FIFO is \(2^{FIFO\_x\_LOG2DEPTH}\). For example if FIFO_x_LOG2DEPTH = 2, the depth of the FIFO is \(2^2 = 4\). FIFO_x_LOG2DEPTH has to be equal or larger than 1, i.e. the minimum depth of the FIFO is 2.

4.3.1.4. Generating Bitstream for DUT

Create a project in Vivado e.g., AES-FBD-A7 and add all source files that you copied in your project directory e.g., AES-DUT and the constraint file.

Select the FPGA device that is on your DUT. You can find that information in the DUT descriptions in Section 4.1.

Make sure that the file core_wrapper_tb.vhd is only used for Simulation. This can be set in the Source File Properties window.

Select the file half_duplex_dut.vhd as the top level. Don’t run synthesis yet!

Some files use the 2008 standard of VHDL. Vivado 2022.1 does not detect this and will create error messages when synthesizing the code. Note: You don’t need to use Vivado 2022.1 to implement the DUT, you can use newer versions but they might still create the same error.

As it is too tedious to set the VHDL revision of all files that use the 2008 standard individually, you can type the following command into the TCL Console of Vivado to apply this to all files.

set_property FILE_TYPE {VHDL 2008} [get_files *.vhd]

Now you can run Synthesis followed by Implementation and Generate Bitstream.

The resulting bit file will be in the directory ~/AES-DUT/AES-FBD-A7/AES-FBD-A7.runs/impl_1 by the name of half_duplex_dut.bit. Copy this file into your Jupyter Notebook directory, maybe under a more descriptive name.

cp ~/AES-DUT/AES-FBD-A7/AES-FBD-A7.runs/impl_1 ~/notebooks/fobos/aes_fbd-a7.bit