blob: 344f40ba89a8d3459e8a6b72693bce0d8b7e988e [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// AES control
module aes_control #(
parameter bit AES192Enable = 1
) (
input logic clk_i,
input logic rst_ni,
// Main control inputs
input aes_pkg::mode_e mode_i,
input aes_pkg::key_len_e key_len_i,
input logic force_data_overwrite_i,
input logic manual_start_trigger_i,
input logic start_i,
// I/O register read/write enables
input logic [3:0] data_in_qe_i,
input logic [7:0] key_init_qe_i,
input logic [3:0] data_out_re_i,
// Control ouptuts cipher data path
output aes_pkg::state_sel_e state_sel_o,
output logic state_we_o,
output aes_pkg::add_rk_sel_e add_rk_sel_o,
// Control outputs key expand data path
output aes_pkg::mode_e key_expand_mode_o,
output aes_pkg::key_full_sel_e key_full_sel_o,
output logic key_full_we_o,
output aes_pkg::key_dec_sel_e key_dec_sel_o,
output logic key_dec_we_o,
output logic key_expand_step_o,
output logic key_expand_clear_o,
output logic [3:0] key_expand_round_o,
output aes_pkg::key_words_sel_e key_words_sel_o,
output aes_pkg::round_key_sel_e round_key_sel_o,
// Output registers control
output logic data_out_we_o,
// To I/O registers
output logic trigger_o,
output logic trigger_we_o,
output logic output_valid_o,
output logic output_valid_we_o,
output logic input_ready_o,
output logic input_ready_we_o,
output logic idle_o,
output logic idle_we_o,
output logic stall_o,
output logic stall_we_o
);
import aes_pkg::*;
// Types
typedef enum logic [1:0] {
IDLE, INIT, ROUND, FINISH
} aes_ctrl_e;
aes_ctrl_e aes_ctrl_ns, aes_ctrl_cs;
// Signals
logic [3:0] data_in_new_d, data_in_new_q;
logic data_in_new;
logic data_in_load;
logic [7:0] key_init_new_d, key_init_new_q;
logic key_init_new;
logic dec_key_gen;
logic [3:0] data_out_read_d, data_out_read_q;
logic data_out_read;
logic output_valid_q;
logic [3:0] round_d, round_q;
logic [3:0] num_rounds_d, num_rounds_q;
logic dec_key_gen_d, dec_key_gen_q;
logic start, clear_key, finish;
assign clear_key = 1'b0;
// If not set to manually start, we start once we have valid data available.
assign start = manual_start_trigger_i ? start_i : data_in_new;
// If not set to overwrite data, we wait for previous output data to be read.
assign finish = force_data_overwrite_i ? 1'b1 : ~output_valid_q;
// FSM
always_comb begin : aes_ctrl_fsm
// Cipher data path
state_sel_o = STATE_ROUND;
state_we_o = 1'b0;
add_rk_sel_o = ADD_RK_ROUND;
// Key expand data path
key_full_sel_o = KEY_FULL_ROUND;
key_full_we_o = 1'b0;
key_dec_sel_o = KEY_DEC_EXPAND;
key_dec_we_o = 1'b0;
key_expand_step_o = 1'b0;
key_expand_clear_o = 1'b0;
key_words_sel_o = KEY_WORDS_ZERO;
round_key_sel_o = ROUND_KEY_DIRECT;
// Trigger register control
trigger_o = 1'b0;
trigger_we_o = 1'b0;
// Status register
idle_o = 1'b0;
idle_we_o = 1'b0;
stall_o = 1'b0;
stall_we_o = 1'b0;
// Key, data I/O register control
dec_key_gen = 1'b0;
data_in_load = 1'b0;
data_out_we_o = 1'b0;
// FSM
aes_ctrl_ns = aes_ctrl_cs;
round_d = round_q;
num_rounds_d = num_rounds_q;
dec_key_gen_d = dec_key_gen_q;
unique case (aes_ctrl_cs)
IDLE: begin
idle_o = 1'b1;
idle_we_o = 1'b1;
stall_o = 1'b0;
stall_we_o = 1'b1;
dec_key_gen_d = 1'b0;
if (start) begin
// We got a new initial key, but want to do decryption.
// We first must get the start key for decryption.
dec_key_gen_d = key_init_new & (mode_i == AES_DEC);
// Load input data to state
state_sel_o = dec_key_gen_d ? STATE_CLEAR : STATE_INIT;
state_we_o = 1'b1;
// Init key expand
key_expand_clear_o = 1'b1;
// Load full key
key_full_sel_o = dec_key_gen_d ? KEY_FULL_ENC_INIT :
(mode_i == AES_ENC) ? KEY_FULL_ENC_INIT :
KEY_FULL_DEC_INIT;
key_full_we_o = 1'b1;
// Load num_rounds, round
round_d = '0;
num_rounds_d = (key_len_i == AES_128) ? 10 :
(key_len_i == AES_192) ? 12 :
14;
idle_o = 1'b0;
idle_we_o = 1'b1;
trigger_we_o = 1'b1;
aes_ctrl_ns = INIT;
end else begin
if (clear_key) begin
key_full_sel_o = KEY_FULL_CLEAR;
key_full_we_o = 1'b1;
key_dec_sel_o = KEY_DEC_CLEAR;
key_dec_we_o = 1'b1;
end
end
end
INIT: begin
// Initial round: just add key to state
state_we_o = ~dec_key_gen_q;
add_rk_sel_o = ADD_RK_INIT;
// Select key words for initial add_round_key
if (dec_key_gen_q) begin
key_words_sel_o = KEY_WORDS_ZERO;
end else begin
unique case (key_len_i)
AES_128: key_words_sel_o = KEY_WORDS_0123;
AES_192: begin
if (AES192Enable) begin
unique case (mode_i)
AES_ENC: key_words_sel_o = KEY_WORDS_0123;
AES_DEC: key_words_sel_o = KEY_WORDS_2345;
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end else begin
key_words_sel_o = key_words_sel_e'(1'bX);
end
end
AES_256: begin
unique case (mode_i)
AES_ENC: key_words_sel_o = KEY_WORDS_0123;
AES_DEC: key_words_sel_o = KEY_WORDS_4567;
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end
// Make key expand advance - AES-256 has two round keys available right from beginning
if (key_len_i != AES_256 ) begin
key_expand_step_o = 1'b1;
key_full_we_o = 1'b1;
end
// Clear data_in_new, key_init_new
data_in_load = ~dec_key_gen_q;
dec_key_gen = dec_key_gen_q;
aes_ctrl_ns = ROUND;
end
ROUND: begin
// Normal rounds
state_we_o = ~dec_key_gen_q;
// Select key words for add_round_key
if (dec_key_gen_q) begin
key_words_sel_o = KEY_WORDS_ZERO;
end else begin
unique case (key_len_i)
AES_128: key_words_sel_o = KEY_WORDS_0123;
AES_192: begin
if (AES192Enable) begin
unique case (mode_i)
AES_ENC: key_words_sel_o = KEY_WORDS_2345;
AES_DEC: key_words_sel_o = KEY_WORDS_0123;
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end else begin
key_words_sel_o = key_words_sel_e'(1'bX);
end
end
AES_256: begin
unique case (mode_i)
AES_ENC: key_words_sel_o = KEY_WORDS_4567;
AES_DEC: key_words_sel_o = KEY_WORDS_0123;
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end
// Make key expand advance
key_expand_step_o = 1'b1;
key_full_we_o = 1'b1;
// Select round key: direct or mixed (equivalent inverse cipher)
round_key_sel_o = (mode_i == AES_ENC) ? ROUND_KEY_DIRECT : ROUND_KEY_MIXED;
// Update round
round_d = round_q+1;
// Are we doing the last regular round?
if (round_q == num_rounds_q-2) begin
if (dec_key_gen_q) begin
// Write decryption key and finish
key_dec_we_o = 1'b1;
dec_key_gen_d = 1'b0;
aes_ctrl_ns = IDLE;
end else begin
aes_ctrl_ns = FINISH;
end
end
end
FINISH: begin
// Final round: do not update state anymore
// Select key words for add_round_key
if (dec_key_gen_q) begin
key_words_sel_o = KEY_WORDS_ZERO;
end else begin
unique case (key_len_i)
AES_128: key_words_sel_o = KEY_WORDS_0123;
AES_192: begin
if (AES192Enable) begin
unique case (mode_i)
AES_ENC: key_words_sel_o = KEY_WORDS_2345;
AES_DEC: key_words_sel_o = KEY_WORDS_0123;
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end else begin
key_words_sel_o = key_words_sel_e'(1'bX);
end
end
AES_256: begin
unique case (mode_i)
AES_ENC: key_words_sel_o = KEY_WORDS_4567;
AES_DEC: key_words_sel_o = KEY_WORDS_0123;
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end
default: key_words_sel_o = key_words_sel_e'(1'bX);
endcase
end
// Skip mix_columns
add_rk_sel_o = ADD_RK_FINAL;
// Write ouput register and clear internal state
if (!finish) begin
stall_o = 1'b1;
stall_we_o = 1'b1;
end else begin
stall_o = 1'b0;
stall_we_o = 1'b1;
data_out_we_o = 1'b1;
aes_ctrl_ns = IDLE;
state_we_o = 1'b1;
state_sel_o = STATE_CLEAR;
end
end
default: aes_ctrl_ns = aes_ctrl_e'(1'bX);
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin : reg_fsm
if (!rst_ni) begin
aes_ctrl_cs <= IDLE;
round_q <= '0;
num_rounds_q <= '0;
dec_key_gen_q <= 1'b0;
end else begin
aes_ctrl_cs <= aes_ctrl_ns;
round_q <= round_d;
num_rounds_q <= num_rounds_d;
dec_key_gen_q <= dec_key_gen_d;
end
end
// Detect new key, new input, output read
// Edge detectors are cleared by the FSM
assign key_init_new_d = dec_key_gen ? '0 : key_init_new_q | key_init_qe_i;
assign key_init_new = &key_init_new_d;
assign data_in_new_d = data_in_load ? '0 : data_in_new_q | data_in_qe_i;
assign data_in_new = &data_in_new_d;
assign data_out_read_d = data_out_we_o ? '0 : data_out_read_q | data_out_re_i;
assign data_out_read = &data_out_read_d;
always_ff @(posedge clk_i or negedge rst_ni) begin : reg_edge_detection
if (!rst_ni) begin
key_init_new_q <= '0;
data_in_new_q <= '0;
data_out_read_q <= '0;
end else begin
key_init_new_q <= key_init_new_d;
data_in_new_q <= data_in_new_d;
data_out_read_q <= data_out_read_d;
end
end
// Clear once all output regs have been read
assign output_valid_o = data_out_we_o;
assign output_valid_we_o = data_out_we_o | data_out_read;
always_ff @(posedge clk_i or negedge rst_ni) begin : reg_output_valid
if (!rst_ni) begin
output_valid_q <= '0;
end else if (output_valid_we_o) begin
output_valid_q <= output_valid_o;
end
end
// Clear once all input regs have been written
assign input_ready_o = ~data_in_new;
assign input_ready_we_o = data_in_new | data_in_load;
assign key_expand_mode_o = (dec_key_gen_d || dec_key_gen_q) ? AES_ENC : mode_i;
assign key_expand_round_o = round_d;
endmodule