This post is a continuation of my previous posts covering my configurable digital logic implementation of the AES-256 cryptographic algorithm. In this post, I implement a block RAM controller wrapper around the AES 256 CTR module. This module will facilitate encryption and decryption of a block RAM memory region accessible from the CDL fabric and the processing system of a target system; something I will cover in the next post.

Block Ram Wrapper

The Counter mode wrapper that was created in the previous post digests a single AES block at a time. To utilize the module, an interface must exist to pass data to and from the AES CTR module. The module covered in this post handles reading, encrypting, and writing 16-byte AES blocks to and from a block RAM.

Implementation

I created a new module, AES_256_CTR_IFACE. The module contains the same ports as the AES CTR module created in the previous post. In addition, the module contains inputs and outputs to read and write from a 128-bit wide block RAM interface.

module AES_256_CTR_IFACE
    (
        input clock,                   //Clock signal
        input enable,                  //Enable signal
        input resetn,                  //Reset signal
        input [255:0] key,             //Input key
        input [127:0] iv,              //Initialization vector
        input [31:0]  base_addr,       //Base address of the data
        input [31:0]  num_blocks,      //Number of AES blocks
        output reg [31:0] bram_addr,   //BRAM Address
        output reg [127:0] bram_din,   //BRAM data in
        input  [127:0] bram_dout,      //BRAM data out
        output reg bram_en,            //BRAM enable
        output reg bram_rst,           //BRAM reset
        output reg [31:0] bram_wen,    //BRAM write enable
        output wire done               //Done signal
    );

The inputs are:

  • clock - The clock signal
  • enable - This input triggers the encryption/decryption of the current block
  • resetn - This signal resets the module.
  • key - The key used for the encryption/decryption
  • iv - The 16 byte initialization vector
  • base_addr - The base address of the block memory region
  • num_blocks - The number of AES blocks to be encrypted
  • bram_addr - The address output for the block RAM interface
  • bram_din - The output port for writing data to the block RAM
  • bram_dout - The input port for reading data from the block RAM
  • bram_en - The enable signal for the block RAM
  • bram_rst - The reset signal for the block RAM
  • bram_wen - The write-enable signal for the block RAM
  • done - Indicates the current operation is done

Next I added a parameter set for the state machine states. These states are covered in detail below.

parameter [7:0] IDLE          = 7'd1,   //State machine idle
                RWORD         = 7'd2,   //Read a 128-bit block
                WAITRWORD     = 7'd3,   //Wait for the read
                CTR_START     = 7'd4,   //Start the CTR module
                WAIT_CTR_DONE = 7'd5,   //Wait for the CTR module to finish
                CTR_STOP      = 7'd6,   //Stop the CTR module
                PREPARE_WWORD = 7'd7,   //Set up for writing the block back to memory
                WWORD         = 7'd8,   //Start the write back
                WAITWWORD     = 7'd9,   //Wait for the write back to finish
                ADV_ADDR      = 7'd10,  //Advance the address to the next block
                DONE          = 7'd11;  //Operation done


Next, registers and wires were added for the signals to and from the AES CTR module.

//Registers for the AES CTR module
reg  ctr_resetn;           //AES CTR reset
reg  ctr_enable;           //AES CTR enable
reg  [127:0] ctr_data_in;  //AES CTR data input
wire [127:0] ctr_data_out; //AES CTR data output
reg  [127:0] ctr_iv;       //AES CTR IV input
reg  [255:0] ctr_key;      //AES CTR key input
wire ctr_valid;            //AES CTR valid signal

Registers were added for keeping track of the state machine state, the number of blocks remaining, the address of the current block, a counter used for clock cycle delays, and the done signal.

// This register holds the current state
reg [7:0] state_reg;

//This register holds the 32-bit words remaining
reg [31:0] blocks_remaining;

//This register holds the current address
reg [31:0] current_addr;

//Stall counter for BRAM read/write
reg [3:0] stall_counter;

//"Done" signal
reg done_reg;
assign done = (done_reg == 1'b1);

As with previous modules, a flip-flop block was added so that the enable input generates a pulse for a single clock cycle.

reg enable_ff1; //Enable pulse flipflops
reg enable_ff2;
assign enable_pulse	= (!enable_ff2) && enable_ff1;

always @(posedge clock) begin                                                        
    if (resetn == 1'b0 || enable == 1'b0)                                                   
        begin
        ///Reset                                                                 
            enable_ff1 <= 1'b0;                                                   
            enable_ff2 <= 1'b0;                                                   
        end                                                                               
    else                                                                       
        begin 
        /// Enable pulse
            enable_ff1 <= enable;
            enable_ff2 <= enable_ff1;                                                        
        end                                                                      
end

Next, an initialization block was added.

//Initialization block
initial begin
    //State reg
    state_reg <= IDLE;
    //Counter module registers
    ctr_resetn = 1'b0;
    ctr_enable = 1'b0;
    ctr_data_in = 127'b0;

    //BRAM stall counter
    stall_counter = 4'b0;
    
    //Enable flip flops
    enable_ff1 = 1'b0;
    enable_ff2 = 1'b0;
end

Next a state machine was created to carry out AES 256 CTR encryption of the block ram. The top part of the block contains reset logic.

    ///Reset logic
    if(resetn == 1'b0) begin
        state_reg <= IDLE;

        //Reset modules
        ctr_resetn <= 1'b0;
        bram_rst <= 1'b1;

        //Counter module registers
        ctr_enable <= 1'b0;
        ctr_data_in <= 127'b0;

        //BRAM control registes
        bram_addr <=  32'b0;
        bram_din  <=  128'b0;
        bram_en   <=  1'b0;
        bram_wen  <=  32'b0;

The first state, IDLE waits for an enable pulse and then latches the values from the input registers into internal registers. The done signal is cleared and the block RAM is enabled.

            IDLE: begin
                if( enable_pulse == 1'b1)
                begin
                    //On the enable pulse, reset the AES core
                    ctr_resetn <= 1'b1;

                    //Reset the BRAM control
                    bram_rst <= 1'b0;
                    
                    //Clear the done signal
                    done_reg <= 1'b0;
                    
                    //Store the base address
                    current_addr <= base_addr;
                    
                    //Store the number of words
                    blocks_remaining <= num_blocks;
                    
                    //Set the key
                    ctr_key <= key;
                    
                    //Set the IV
                    ctr_iv <= iv;

                    //Reset the stall ctr
                    stall_counter <= 4'b0;

                    //Enable the BRAM
                    bram_en <= 1'b1;
                    
                    //Move to set words
                    state_reg <= RWORD;
                end
            end

The next state sets up a read of the block RAM by providing the address of the current AES block.

    RWORD: begin
        //Set the read address
        bram_addr <= current_addr;

        //Move to wait word 1
        state_reg <= WAITRWORD;
    end

Next the WAITRWORD state stalls for several clock cycles while the block RAM read is in progress. Afterward, the block is placed into the input of the AES CTR module.

WAITRWORD: begin
    if(stall_counter < 4'h3) begin
        stall_counter <= stall_counter + 1; 
    end
    else begin
        //Store the read word into the CTR input
        ctr_data_in <= bram_dout;

        //Reset the stall counter
        stall_counter <= 4'b0;

        //Decrement the words remaining
        blocks_remaining <= blocks_remaining - 1;

        //Move to the next word
        state_reg <= CTR_START;
    end
end

The AES CTR process is started in the CTR_START state. This block stalls for several cycles before moving to the next state where the valid bit is checked.

CTR_START: begin
                
    //Enable the counter module
    ctr_enable <= 1'b1;
    
    //Stall before checking done 
    if(stall_counter < 4'h3) begin
        stall_counter <= stall_counter + 1; 
    end
    else
    begin
        //Reset the stall counter
        stall_counter <= 0;

        //Move to the wait done state
        state_reg <= WAIT_CTR_DONE;

    end
end

The WAIT_CTR_DONE state allows the AES CTR module to finish before advancing to the CTR_STOP state.

 WAIT_CTR_DONE: begin
    if(ctr_valid == 1'b1) begin
        state_reg <= CTR_STOP;
    end
end

The AES CTR module is disabled in the CTR_STOP state.

CTR_STOP: begin
    //Disable the counter
    ctr_enable <= 1'b0;
    state_reg <= PREPARE_WWORD;
end

Next, the state machine prepares to write the encrypted block back into the block RAM in the PREPARE_WWORD. In the subsequent WWORD state, the BRAM write-enable signal is asserted.

PREPARE_WWORD: begin
    //Set the block ram address
    bram_addr <= current_addr;

    //Set the input data
    bram_din <= ctr_data_out;

    state_reg <= WWORD;
end
WWORD: begin
    //Set the write enable
    bram_wen <= 32'hFFFFFFFF;

    //Wait for the word
    state_reg <= WAITWWORD;
end

Another wait block, WWORD, was added to allow the write process to complete.

WAITWWORD: begin
    //Stall before checking done 
    if(stall_counter < 4'h3) begin
        stall_counter <= stall_counter + 1; 
    end
    else begin
        //Clear the write enable
        bram_wen <= 32'b0;
        
        state_reg <= ADV_ADDR;
    end
end

A state, ADV_ADDR, was added to advance the block address. If there are no blocks remaining to be encrypted, the state machine is advanced to the DONE state. Otherwise, the state is returned to the RRWORD state. The DONE state asserts the done signal.

ADV_ADDR: begin
        //Reset the stall counter
        stall_counter <= 0;

        //Increment the address
        current_addr <= current_addr + 16;
        
        //Check if there are more words to encrypt
        if(blocks_remaining == 31'b0) begin
            state_reg <= DONE;
        end
        else begin
            state_reg <= RWORD;
        end
end
DONE: begin
    

    //Reset the AES core
    ctr_resetn <= 1'b0;

    //Reset the BRAM control
    bram_rst <= 1'b1;
    
    //Set the done signal
    done_reg <= 1'b1;
    //Return to the IDLE state
    state_reg <= IDLE;
end

Concept of Operation

This module is designed to interface with a processor via command/control registers and a block RAM module. The caller must place the data to be encrypted or decrypted in the block RAM memory that is attached to the module. Next the key, iv, and number of blocks are written via the command/control interface. When enabled, the module will read the data from the Block RAM, encrypt it, and write it back. When the process is complete, the done signal is asserted and is observable by the caller in the status register. This design will be discussed in depth in the next post.

Summary

The AES implementation is nearly complete. In the next post, this hierarchy of modules will be added to an AXI-4 peripheral and run on the Zynq700 FPGA.

Get honeypotted? I like spam. Contact Us Contact Us Email Email ar.hp@outlook.com email: ar.hp@outlook.com