blob: 9efa4f0b878634fdfff793b531eb14dc8a11fdea [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 main control
//
// This module controls the interplay of input/output registers and the AES cipher core.
`include "prim_assert.sv"
module aes_control (
input logic clk_i,
input logic rst_ni,
// Main control inputs
input aes_pkg::aes_op_e op_i,
input aes_pkg::aes_mode_e mode_i,
input aes_pkg::ciph_op_e cipher_op_i,
input logic manual_operation_i,
input logic ctrl_err_i,
input logic start_i,
input logic key_clear_i,
input logic iv_clear_i,
input logic data_in_clear_i,
input logic data_out_clear_i,
input logic prng_reseed_i,
// I/O register read/write enables
input logic [7:0] key_init_qe_i,
input logic [3:0] iv_qe_i,
input logic [3:0] data_in_qe_i,
input logic [3:0] data_out_re_i,
output logic data_in_we_o,
output logic data_out_we_o,
// Previous input data register
output aes_pkg::dip_sel_e data_in_prev_sel_o,
output logic data_in_prev_we_o,
// Cipher I/O muxes
output aes_pkg::si_sel_e state_in_sel_o,
output aes_pkg::add_si_sel_e add_state_in_sel_o,
output aes_pkg::add_so_sel_e add_state_out_sel_o,
// Counter
output logic ctr_incr_o,
input logic ctr_ready_i,
input logic [7:0] ctr_we_i,
// Cipher core control and sync
output logic cipher_in_valid_o,
input logic cipher_in_ready_i,
input logic cipher_out_valid_i,
output logic cipher_out_ready_o,
output logic cipher_crypt_o,
input logic cipher_crypt_i,
output logic cipher_dec_key_gen_o,
input logic cipher_dec_key_gen_i,
output logic cipher_key_clear_o,
input logic cipher_key_clear_i,
output logic cipher_data_out_clear_o,
input logic cipher_data_out_clear_i,
// Initial key registers
output aes_pkg::key_init_sel_e key_init_sel_o,
output logic [7:0] key_init_we_o,
// IV registers
output aes_pkg::iv_sel_e iv_sel_o,
output logic [7:0] iv_we_o,
// Pseudo-random number generator interface
output logic prng_data_req_o,
input logic prng_data_ack_i,
output logic prng_reseed_req_o,
input logic prng_reseed_ack_i,
// Trigger register
output logic start_o,
output logic start_we_o,
output logic key_clear_o,
output logic key_clear_we_o,
output logic iv_clear_o,
output logic iv_clear_we_o,
output logic data_in_clear_o,
output logic data_in_clear_we_o,
output logic data_out_clear_o,
output logic data_out_clear_we_o,
output logic prng_reseed_o,
output logic prng_reseed_we_o,
// Status register
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 [2:0] {
IDLE, LOAD, UPDATE_PRNG, FINISH, CLEAR
} aes_ctrl_e;
aes_ctrl_e aes_ctrl_ns, aes_ctrl_cs;
// Signals
logic key_init_clear;
logic key_init_new;
logic dec_key_gen;
logic key_init_ready;
logic [7:0] iv_qe;
logic iv_clear;
logic iv_load;
logic iv_ready;
logic [3:0] data_in_new_d, data_in_new_q;
logic data_in_new;
logic data_in_load;
logic [3:0] data_out_read_d, data_out_read_q;
logic data_out_read;
logic output_valid_q;
logic cfg_valid;
logic start, finish;
logic cipher_crypt;
logic doing_cbc_enc, doing_cbc_dec;
logic doing_ctr;
// Software updates IV in chunks of 32 bits, the counter updates 16 bits at a time.
// Convert word write enable to internal half-word write enable.
assign iv_qe = {iv_qe_i[3], iv_qe_i[3], iv_qe_i[2], iv_qe_i[2],
iv_qe_i[1], iv_qe_i[1], iv_qe_i[0], iv_qe_i[0]};
// The cipher core is only ever allowed to start or finish if the control register holds a valid
// configuration.
assign cfg_valid = ~((mode_i == AES_NONE) | ctrl_err_i);
// If set to start manually, we just wait for the trigger. Otherwise, we start once we have valid
// data available. If the IV (and counter) is needed, we only start if also the IV (and counter)
// is ready.
assign start = cfg_valid &
( manual_operation_i ? start_i :
(mode_i == AES_ECB) ? (key_init_ready & data_in_new) :
(mode_i == AES_CBC) ? (key_init_ready & data_in_new & iv_ready) :
(mode_i == AES_CTR) ? (key_init_ready & data_in_new & iv_ready & ctr_ready_i) : 1'b0);
// If not set to overwrite data, we wait for any previous output data to be read. data_out_read
// synchronously clears output_valid_q, unless new output data is written in the exact same
// clock cycle.
assign finish = cfg_valid & (manual_operation_i ? 1'b1 : (~output_valid_q | data_out_read));
// Helper signals for FSM
assign cipher_crypt = cipher_crypt_o | cipher_crypt_i;
assign doing_cbc_enc = cipher_crypt & (mode_i == AES_CBC) & (op_i == AES_ENC);
assign doing_cbc_dec = cipher_crypt & (mode_i == AES_CBC) & (op_i == AES_DEC);
assign doing_ctr = cipher_crypt & (mode_i == AES_CTR);
// FSM
always_comb begin : aes_ctrl_fsm
// Previous input data register control
data_in_prev_sel_o = DIP_CLEAR;
data_in_prev_we_o = 1'b0;
// Cipher I/O mux control
state_in_sel_o = SI_DATA;
add_state_in_sel_o = ADD_SI_ZERO;
add_state_out_sel_o = ADD_SO_ZERO;
// Counter control
ctr_incr_o = 1'b0;
// Cipher core control
cipher_in_valid_o = 1'b0;
cipher_out_ready_o = 1'b0;
cipher_crypt_o = 1'b0;
cipher_dec_key_gen_o = 1'b0;
cipher_key_clear_o = 1'b0;
cipher_data_out_clear_o = 1'b0;
// Initial key registers
key_init_sel_o = KEY_INIT_INPUT;
key_init_we_o = 8'h00;
// IV registers
iv_sel_o = IV_INPUT;
iv_we_o = 8'h00;
iv_load = 1'b0;
// Pseudo-random number generator control
prng_data_req_o = 1'b0;
prng_reseed_req_o = 1'b0;
// Trigger register control
start_we_o = 1'b0;
key_clear_we_o = 1'b0;
iv_clear_we_o = 1'b0;
data_in_clear_we_o = 1'b0;
data_out_clear_we_o = 1'b0;
prng_reseed_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_in_we_o = 1'b0;
data_out_we_o = 1'b0;
// Edge detector control
key_init_clear = 1'b0;
iv_clear = 1'b0;
// FSM
aes_ctrl_ns = aes_ctrl_cs;
unique case (aes_ctrl_cs)
IDLE: begin
idle_o = (start || key_clear_i || iv_clear_i ||
data_in_clear_i || data_out_clear_i || prng_reseed_i) ? 1'b0 : 1'b1;
idle_we_o = 1'b1;
// Initial key and IV updates are ignored if we are not idle.
key_init_we_o = idle_o ? key_init_qe_i : 8'h00;
iv_we_o = idle_o ? iv_qe : 8'h00;
if (prng_reseed_i) begin
// Request a reseed of the PRNG, perform handshake.
prng_reseed_req_o = 1'b1;
if (prng_reseed_ack_i) begin
// Clear the trigger.
prng_reseed_we_o = 1'b1;
end
end else if (key_clear_i || data_out_clear_i || iv_clear_i || data_in_clear_i) begin
// To clear registers, we must first request fresh pseudo-random data.
aes_ctrl_ns = UPDATE_PRNG;
end else if (start) begin
// Signal that we want to start encryption/decryption.
cipher_crypt_o = 1'b1;
// We got a new initial key, but want to do decryption. The cipher core must first
// generate the start key for decryption.
cipher_dec_key_gen_o = key_init_new & (cipher_op_i == CIPH_INV);
// Previous input data register control
data_in_prev_sel_o = doing_cbc_dec ? DIP_DATA_IN :
doing_ctr ? DIP_DATA_IN : DIP_CLEAR;
data_in_prev_we_o = doing_cbc_dec ? 1'b1 :
doing_ctr ? 1'b1 : 1'b0;
// State input mux control
state_in_sel_o = doing_ctr ? SI_ZERO : SI_DATA;
// State input additon mux control
add_state_in_sel_o = doing_cbc_enc ? ADD_SI_IV :
doing_ctr ? ADD_SI_IV : ADD_SI_ZERO;
// We have work for the cipher core, perform handshake.
cipher_in_valid_o = 1'b1;
if (cipher_in_ready_i) begin
// Do not yet clear a possible start trigger if we are just starting the generation of
// the start key for decryption.
start_we_o = ~cipher_dec_key_gen_o;
aes_ctrl_ns = LOAD;
end
end
end
LOAD: begin
// Clear key_init_new, iv_new, data_in_new
dec_key_gen = cipher_dec_key_gen_i;
iv_load = ~cipher_dec_key_gen_i;
data_in_load = ~cipher_dec_key_gen_i;
// Trigger counter increment.
ctr_incr_o = doing_ctr ? 1'b1 : 1'b0;
// Unless we are just generating the start key for decryption, we must update the PRNG.
aes_ctrl_ns = ~cipher_dec_key_gen_i ? UPDATE_PRNG : FINISH;
end
UPDATE_PRNG: begin
// Fresh pseudo-random data is used to:
// - clear the state in the final cipher round,
// - clear any other registers in the CLEAR state.
// IV control in case of ongoing encryption/decryption
// - CTR: IV registers are updated by counter during cipher operation
iv_sel_o = doing_ctr ? IV_CTR : IV_INPUT;
iv_we_o = doing_ctr ? ctr_we_i : 8'h00;
// Request fresh pseudo-random data, perform handshake.
prng_data_req_o = 1'b1;
if (prng_data_ack_i) begin
// Ongoing encryption/decryption operations have the highest priority. The clear triggers
// might have become asserted after the handshake with the cipher core.
if (cipher_crypt_i) begin
aes_ctrl_ns = FINISH;
end else if (key_clear_i || data_out_clear_i) begin
// To clear the output data registers, we re-use the muxing resources of the cipher
// core. To clear all key material, some key registers inside the cipher core need to
// be cleared.
cipher_key_clear_o = key_clear_i;
cipher_data_out_clear_o = data_out_clear_i;
// We have work for the cipher core, perform handshake.
cipher_in_valid_o = 1'b1;
if (cipher_in_ready_i) begin
aes_ctrl_ns = CLEAR;
end
end else begin // (iv_clear_i || data_in_clear_i)
// To clear the IV or input data registers, no handshake with the cipher core is
// needed.
aes_ctrl_ns = CLEAR;
end
end
end
FINISH: begin
// Wait for cipher core to finish.
if (cipher_dec_key_gen_i) begin
// We are ready.
cipher_out_ready_o = 1'b1;
if (cipher_out_valid_i) begin
aes_ctrl_ns = IDLE;
end
end else begin
// Signal if the cipher core is stalled (because previous output has not yet been read).
stall_o = ~finish & cipher_out_valid_i;
stall_we_o = 1'b1;
// State out addition mux control
add_state_out_sel_o = doing_cbc_dec ? ADD_SO_IV :
doing_ctr ? ADD_SO_DIP : ADD_SO_ZERO;
// IV control
// - CBC: IV registers can only be updated when cipher finishes
// - CTR: IV registers are updated by counter during cipher operation
iv_sel_o = doing_cbc_enc ? IV_DATA_OUT :
doing_cbc_dec ? IV_DATA_IN_PREV :
doing_ctr ? IV_CTR : IV_INPUT;
iv_we_o = (doing_cbc_enc || doing_cbc_dec) ? {8{finish & cipher_out_valid_i}} :
doing_ctr ? ctr_we_i : 8'h00;
// We are ready once the output data registers can be written.
cipher_out_ready_o = finish;
if (finish & cipher_out_valid_i) begin
data_out_we_o = 1'b1;
aes_ctrl_ns = IDLE;
end
end
end
CLEAR: begin
// The IV and input data registers can be cleared independently of the cipher core.
if (iv_clear_i) begin
iv_sel_o = IV_CLEAR;
iv_we_o = 8'hFF;
iv_clear_we_o = 1'b1;
iv_clear = 1'b1;
end
if (data_in_clear_i) begin
data_in_we_o = 1'b1;
data_in_clear_we_o = 1'b1;
data_in_prev_sel_o = DIP_CLEAR;
data_in_prev_we_o = 1'b1;
end
// To clear the output data registers, we re-use the muxing resources of the cipher core.
// To clear all key material, some key registers inside the cipher core need to be cleared.
if (cipher_key_clear_i || cipher_data_out_clear_i) begin
// Perform handshake.
cipher_out_ready_o = 1'b1;
if (cipher_out_valid_i) begin
if (cipher_key_clear_i) begin
key_init_sel_o = KEY_INIT_CLEAR;
key_init_we_o = 8'hFF;
key_clear_we_o = 1'b1;
key_init_clear = 1'b1;
end
if (cipher_data_out_clear_i) begin
data_out_we_o = 1'b1;
data_out_clear_we_o = 1'b1;
end
aes_ctrl_ns = IDLE;
end
end else begin
aes_ctrl_ns = IDLE;
end
end
default: aes_ctrl_ns = IDLE;
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin : reg_fsm
if (!rst_ni) begin
aes_ctrl_cs <= IDLE;
end else begin
aes_ctrl_cs <= aes_ctrl_ns;
end
end
// We only use clean initial keys. Either software/counter has updated
// - all initial key registers, or
// - none of the initial key registers but the registers were updated in the past.
aes_reg_status #(
.Width ( $bits(key_init_we_o) )
) u_reg_status_key_init (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.we_i ( key_init_we_o ),
.use_i ( dec_key_gen ),
.clear_i ( key_init_clear ),
.new_o ( key_init_new ),
.clean_o ( key_init_ready )
);
// We only use clean and unused IVs. Either software/counter has updated
// - all IV registers, or
// - none of the IV registers but the registers were updated in the past
// and this particular IV has not yet been used.
aes_reg_status #(
.Width ( $bits(iv_we_o) )
) u_reg_status_iv (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.we_i ( iv_we_o ),
.use_i ( iv_load ),
.clear_i ( iv_clear ),
.new_o ( iv_ready ),
.clean_o ( )
);
// Detect new input and output read.
// Edge detectors are cleared by the FSM.
assign data_in_new_d = (data_in_load || data_in_we_o) ? '0 : (data_in_new_q | data_in_qe_i);
assign data_in_new = &data_in_new_d;
// data_out_read is high for one clock cycle only. It clears output_valid_q unless new output
// data is written in the exact same cycle.
assign data_out_read_d = &data_out_read_q ? '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
data_in_new_q <= '0;
data_out_read_q <= '0;
end else begin
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, or when output is cleared
assign output_valid_o = data_out_we_o & ~data_out_clear_we_o;
assign output_valid_we_o = data_out_we_o | data_out_read | data_out_clear_we_o;
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, or when input clear is requested
assign input_ready_o = ~data_in_new;
assign input_ready_we_o = data_in_new | data_in_load | data_in_we_o;
// Trigger register, the control only ever clears these
assign start_o = 1'b0;
assign key_clear_o = 1'b0;
assign iv_clear_o = 1'b0;
assign data_in_clear_o = 1'b0;
assign data_out_clear_o = 1'b0;
assign prng_reseed_o = 1'b0;
// Selectors must be known/valid
`ASSERT(AesModeValid, mode_i inside {
AES_ECB,
AES_CBC,
AES_CTR,
AES_NONE
})
`ASSERT_KNOWN(AesOpKnown, op_i)
`ASSERT_KNOWN(AesCiphOpKnown, cipher_op_i)
`ASSERT_KNOWN(AesControlStateValid, aes_ctrl_cs)
endmodule