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.
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.
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 signalenable
- This input triggers the encryption/decryption of the current blockresetn
- This signal resets the module.key
- The key used for the encryption/decryptioniv
- The 16 byte initialization vectorbase_addr
- The base address of the block memory regionnum_blocks
- The number of AES blocks to be encryptedbram_addr
- The address output for the block RAM interfacebram_din
- The output port for writing data to the block RAMbram_dout
- The input port for reading data from the block RAMbram_en
- The enable signal for the block RAMbram_rst
- The reset signal for the block RAMbram_wen
- The write-enable signal for the block RAMdone
- Indicates the current operation is doneNext 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
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.
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.