[aes] Use one-hot encoding for OPERATION field in main control register

This is related to lowRISC/OpenTitan#10422.

Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/ip/aes/data/aes.hjson b/hw/ip/aes/data/aes.hjson
index aaf4603..3f34e59 100644
--- a/hw/ip/aes/data/aes.hjson
+++ b/hw/ip/aes/data/aes.hjson
@@ -372,15 +372,31 @@
     update_err_alert: "recov_ctrl_update_err",
     storage_err_alert: "fatal_fault",
     fields: [
-      { bits: "0",
+      { bits: "1:0",
         name: "OPERATION",
-        resval: "0",
+        resval: "0x1",
+        hwaccess: "hrw",
         desc:  '''
-          Select encryption(0) or decryption(1) operation of AES unit.
+          2-bit one-hot field to select the operation of AES unit.
+          Invalid input values, i.e., values with multiple bits set and value 2'b00, are mapped to AES_ENCRYPTION (2'b01).
         '''
+        enum: [
+          { value: "1",
+            name: "AES_ENC",
+            desc: '''
+              2'b01: Encryption.
+            '''
+          },
+          { value: "2",
+            name: "AES_DEC",
+            desc: '''
+              2'b10: Decryption.
+            '''
+          },
+        ]
         tags: ["shadowed_reg_path:u_aes_core.u_ctrl_reg_shadowed.u_ctrl_reg_shadowed_operation"]
       }
-      { bits: "6:1",
+      { bits: "7:2",
         name: "MODE",
         resval: "0x20",
         hwaccess: "hrw",
@@ -428,7 +444,7 @@
         ]
         tags: ["shadowed_reg_path:u_aes_core.u_ctrl_reg_shadowed.u_ctrl_reg_shadowed_mode"]
       }
-      { bits: "9:7",
+      { bits: "10:8",
         name: "KEY_LEN",
         resval: "1",
         hwaccess: "hrw",
@@ -460,7 +476,7 @@
         ]
         tags: ["shadowed_reg_path:u_aes_core.u_ctrl_reg_shadowed.u_ctrl_reg_shadowed_key_len"]
       }
-      { bits: "10",
+      { bits: "11",
         name: "SIDELOAD",
         resval: "0",
         desc: '''
@@ -468,7 +484,7 @@
         '''
         tags: ["shadowed_reg_path:u_aes_core.u_ctrl_reg_shadowed.u_ctrl_reg_shadowed_sideload"]
       }
-      { bits: "13:11",
+      { bits: "14:12",
         name: "PRNG_RESEED_RATE",
         resval: "1",
         hwaccess: "hrw",
@@ -499,7 +515,7 @@
         ]
         tags: ["shadowed_reg_path:u_aes_core.u_ctrl_reg_shadowed.u_ctrl_reg_shadowed_prng_reseed_rate"]
       }
-      { bits: "14",
+      { bits: "15",
         name: "MANUAL_OPERATION",
         resval: "0"
         desc:  '''
@@ -512,7 +528,7 @@
         '''
         tags: ["shadowed_reg_path:u_aes_core.u_ctrl_reg_shadowed.u_ctrl_reg_shadowed_manual_operation"]
       }
-      { bits: "15",
+      { bits: "16",
         name: "FORCE_ZERO_MASKS",
         resval: "0"
         desc:  '''
diff --git a/hw/ip/aes/dv/env/aes_scoreboard.sv b/hw/ip/aes/dv/env/aes_scoreboard.sv
index 1a1e4d8..9b0ab31 100644
--- a/hw/ip/aes/dv/env/aes_scoreboard.sv
+++ b/hw/ip/aes/dv/env/aes_scoreboard.sv
@@ -529,6 +529,7 @@
     bit [3:0][31:0] tmp_output;
 
     forever begin
+      bit operation;
       aes_message_item msg;
       msg_fifo.get(msg);
       `uvm_info(`gfn, $sformatf("model %b, operation: %b, mode %06b, IV %h, key_len %03b, key share0 %h, key share1 %h ",
@@ -540,7 +541,9 @@
         msg.alloc_predicted_msg();
 
         //ref-model      / opration     / chipher mode /    IV   / key_len    / key /data i /data o //
-        c_dpi_aes_crypt_message(cfg.ref_model, msg.aes_operation, msg.aes_mode, msg.aes_iv,
+        operation = msg.aes_operation == AES_ENC ? 1'b0 :
+                    msg.aes_operation == AES_DEC ? 1'b1 : 1'b0;
+        c_dpi_aes_crypt_message(cfg.ref_model, operation, msg.aes_mode, msg.aes_iv,
                                 msg.aes_keylen, msg.aes_key[0] ^ msg.aes_key[1],
                                 msg.input_msg, msg.predicted_msg);
 
diff --git a/hw/ip/aes/dv/env/seq_lib/aes_base_vseq.sv b/hw/ip/aes/dv/env/seq_lib/aes_base_vseq.sv
index 1d83d4e..9d477f5 100644
--- a/hw/ip/aes/dv/env/seq_lib/aes_base_vseq.sv
+++ b/hw/ip/aes/dv/env/seq_lib/aes_base_vseq.sv
@@ -56,9 +56,9 @@
      // Wait for DUT ready
     csr_spinwait(.ptr(ral.status.idle) , .exp_data(1'b1));
     // initialize control register
-    aes_ctrl[0]    = 0;                  // set to encryption
-    aes_ctrl[6:1]  = aes_pkg::AES_ECB;   // 6'b00_0001
-    aes_ctrl[9:7]  = aes_pkg::AES_128;   // set to 128b key
+    aes_ctrl[1:0]  = aes_pkg::AES_ENC;   // 2'b01
+    aes_ctrl[7:2]  = aes_pkg::AES_ECB;   // 6'b00_0001
+    aes_ctrl[10:8] = aes_pkg::AES_128;   // 3'b001
     csr_wr(.ptr(ral.ctrl_shadowed), .value(aes_ctrl), .en_shadow_wr(1'b1), .blocking(1));
     // initialize aux control register and lock it
     // This is a temporary workaround until the aux control register is properly supported.
@@ -94,7 +94,7 @@
   endtask // prng_reseed
 
 
-  virtual task set_operation(bit operation);
+  virtual task set_operation(bit [1:0] operation);
       ral.ctrl_shadowed.operation.set(operation);
       csr_update(.csr(ral.ctrl_shadowed), .en_shadow_wr(1'b1), .blocking(1));
   endtask // set_operation
diff --git a/hw/ip/aes/dv/env/seq_lib/aes_wake_up_vseq.sv b/hw/ip/aes/dv/env/seq_lib/aes_wake_up_vseq.sv
index 23f606a..02806b7 100644
--- a/hw/ip/aes/dv/env/seq_lib/aes_wake_up_vseq.sv
+++ b/hw/ip/aes/dv/env/seq_lib/aes_wake_up_vseq.sv
@@ -10,9 +10,6 @@
   `uvm_object_new
 
 
-  parameter bit       ENCRYPT = 1'b0;
-  parameter bit       DECRYPT = 1'b1;
-
   bit [3:0] [31:0]    plain_text       = 128'hDEADBEEFEEDDBBAABAADBEEFDEAFBEAD;
   logic [255:0]       init_key [2]     = '{256'h0000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF, 256'h0};
   bit [3:0] [31:0]    cypher_text;
@@ -31,7 +28,7 @@
 
     `uvm_info(`gfn, $sformatf(" \n\t ---|setting operation to encrypt"), UVM_HIGH)
     // set operation to encrypt
-    set_operation(ENCRYPT);
+    set_operation(AES_ENC);
 
     `uvm_info(`gfn, $sformatf(" \n\t ---| WRITING INIT KEY \n\t ----| SHARE0 %02h \n\t ---| SHARE1 %02h ", init_key[0], init_key[1]), UVM_HIGH)
     write_key(init_key, do_b2b);
@@ -54,7 +51,7 @@
     cfg.clk_rst_vif.wait_clks(20);
 
     // set aes to decrypt
-    set_operation(DECRYPT);
+    set_operation(AES_DEC);
     cfg.clk_rst_vif.wait_clks(20);
     `uvm_info(`gfn, $sformatf("\n\t ---| WRITING INIT KEY \n\t ----| SHARE0 %02h \n\t ---| SHARE1 %02h ", init_key[0], init_key[1]), UVM_HIGH)
     write_key(init_key, do_b2b);
diff --git a/hw/ip/aes/rtl/aes_cipher_control.sv b/hw/ip/aes/rtl/aes_cipher_control.sv
index 425d935..7cd503a 100644
--- a/hw/ip/aes/rtl/aes_cipher_control.sv
+++ b/hw/ip/aes/rtl/aes_cipher_control.sv
@@ -40,6 +40,7 @@
   output logic                    data_out_clear_o,
   input  logic                    mux_sel_err_i,
   input  logic                    sp_enc_err_i,
+  input  logic                    op_err_i,
   output logic                    alert_o,
 
   // Control signals for masking PRNG
@@ -176,6 +177,7 @@
         .mux_sel_err_i         ( mux_sel_err              ),
         .sp_enc_err_i          ( sp_enc_err               ),
         .rnd_ctr_err_i         ( rnd_ctr_err              ),
+        .op_err_i              ( op_err_i                 ),
         .alert_o               ( mr_alert[i]              ), // OR-combine
 
         .prng_update_o         ( mr_prng_update[i]        ), // OR-combine
@@ -242,6 +244,7 @@
         .mux_sel_err_i         ( mux_sel_err              ),
         .sp_enc_err_i          ( sp_enc_err               ),
         .rnd_ctr_err_i         ( rnd_ctr_err              ),
+        .op_err_i              ( op_err_i                 ),
         .alert_o               ( mr_alert[i]              ), // OR-combine
 
         .prng_update_o         ( mr_prng_update[i]        ), // OR-combine
@@ -538,6 +541,9 @@
   ////////////////
 
   // Selectors must be known/valid
-  `ASSERT_KNOWN(AesCiphOpKnown, op_i)
+  `ASSERT(AesCiphOpValid, cfg_valid_i |-> op_i inside {
+      CIPH_FWD,
+      CIPH_INV
+      })
 
 endmodule
diff --git a/hw/ip/aes/rtl/aes_cipher_control_fsm.sv b/hw/ip/aes/rtl/aes_cipher_control_fsm.sv
index 566b996..bb391e8 100644
--- a/hw/ip/aes/rtl/aes_cipher_control_fsm.sv
+++ b/hw/ip/aes/rtl/aes_cipher_control_fsm.sv
@@ -35,6 +35,7 @@
   input  logic             data_out_clear_i,
   input  logic             mux_sel_err_i,
   input  logic             sp_enc_err_i,
+  input  logic             op_err_i,
   output logic             alert_o,
 
   // Control signals for masking PRNG
@@ -226,7 +227,8 @@
             // Load full key
             key_full_sel_o = dec_key_gen_i ? KEY_FULL_ENC_INIT :
                         (op_i == CIPH_FWD) ? KEY_FULL_ENC_INIT :
-                                             KEY_FULL_DEC_INIT;
+                        (op_i == CIPH_INV) ? KEY_FULL_DEC_INIT :
+                                             KEY_FULL_ENC_INIT;
             key_full_we_o  = 1'b1;
 
             // Load num_rounds, initialize round counters.
@@ -304,7 +306,8 @@
         prng_reseed_req_o = Masking & prng_reseed_q_i & ~prng_reseed_done_q;
 
         // Select round key: direct or mixed (equivalent inverse cipher)
-        round_key_sel_o = (op_i == CIPH_FWD) ? ROUND_KEY_DIRECT : ROUND_KEY_MIXED;
+        round_key_sel_o = (op_i == CIPH_FWD) ? ROUND_KEY_DIRECT :
+                          (op_i == CIPH_INV) ? ROUND_KEY_MIXED  : ROUND_KEY_DIRECT;
 
         // Advance in sync with SubBytes and KeyExpand. Based on the S-Box implementation, both can
         // take multiple cycles to finish. Wait for handshake. The DOM S-Boxes consume fresh PRD
@@ -382,7 +385,7 @@
         // Perform both handshakes simultaneously.
         advance        = sub_bytes_out_req_i | dec_key_gen_q_i;
         sub_bytes_en_o = ~dec_key_gen_q_i;
-        out_valid_o    = (mux_sel_err_i || sp_enc_err_i) ? 1'b0                  :
+        out_valid_o    = (mux_sel_err_i || sp_enc_err_i || op_err_i) ? 1'b0      :
             Masking ? (prng_reseed_q_i ? prng_reseed_done_q & advance : advance) : advance;
 
         // Stop updating the cycle counter once we have valid output.
@@ -466,7 +469,7 @@
 
     // Unconditionally jump into the terminal error state in case a mux selector or a sparsely
     // encoded signal becomes invalid, or in case we have detected a fault in the round counter.
-    if (mux_sel_err_i || sp_enc_err_i || rnd_ctr_err_i) begin
+    if (mux_sel_err_i || sp_enc_err_i || rnd_ctr_err_i || op_err_i) begin
       aes_cipher_ctrl_ns = ERROR;
     end
   end
@@ -513,7 +516,10 @@
   ////////////////
 
   // Selectors must be known/valid
-  `ASSERT_KNOWN(AesCiphOpKnown, op_i)
+  `ASSERT(AesCiphOpValid, cfg_valid_i |-> op_i inside {
+      CIPH_FWD,
+      CIPH_INV
+      })
   `ASSERT(AesKeyLenValid, cfg_valid_i |-> key_len_i inside {
       AES_128,
       AES_192,
diff --git a/hw/ip/aes/rtl/aes_cipher_control_fsm_n.sv b/hw/ip/aes/rtl/aes_cipher_control_fsm_n.sv
index 629a689..fe20f41 100644
--- a/hw/ip/aes/rtl/aes_cipher_control_fsm_n.sv
+++ b/hw/ip/aes/rtl/aes_cipher_control_fsm_n.sv
@@ -39,6 +39,7 @@
   input  logic             mux_sel_err_i,
   input  logic             sp_enc_err_i,
   input  logic             rnd_ctr_err_i,
+  input  logic             op_err_i,
   output logic             alert_o,
 
   // Control signals for masking PRNG
@@ -103,6 +104,7 @@
     mux_sel_err_i,
     sp_enc_err_i,
     rnd_ctr_err_i,
+    op_err_i,
     prng_reseed_ack_i,
     sub_bytes_out_req_ni,
     key_expand_out_req_ni,
@@ -132,6 +134,7 @@
     mux_sel_err_i,
     sp_enc_err_i,
     rnd_ctr_err_i,
+    op_err_i,
     prng_reseed_ack_i,
     sub_bytes_out_req_ni,
     key_expand_out_req_ni,
@@ -154,31 +157,32 @@
     .out_o(in_buf)
   );
 
-  logic             in_valid_n;
-  logic             out_ready_n;
-  logic             cfg_valid;
-  logic             op_raw;
-  ciph_op_e         op;
-  key_len_e         key_len;
-  logic             crypt_n;
-  logic             dec_key_gen_n;
-  logic             prng_reseed;
-  logic             key_clear;
-  logic             data_out_clear;
-  logic             mux_sel_err;
-  logic             sp_enc_err;
-  logic             rnd_ctr_err;
-  logic             prng_reseed_ack;
-  logic             sub_bytes_out_req_n;
-  logic             key_expand_out_req_n;
-  logic [3:0]       rnd_ctr_q;
-  logic [3:0]       rnd_ctr_rem_q;
-  logic [3:0]       num_rounds_q;
-  logic             crypt_q_n;
-  logic             dec_key_gen_q_n;
-  logic             prng_reseed_q;
-  logic             key_clear_q;
-  logic             data_out_clear_q;
+  logic                 in_valid_n;
+  logic                 out_ready_n;
+  logic                 cfg_valid;
+  ciph_op_e             op;
+  logic [$bits(op)-1:0] op_raw;
+  key_len_e             key_len;
+  logic                 crypt_n;
+  logic                 dec_key_gen_n;
+  logic                 prng_reseed;
+  logic                 key_clear;
+  logic                 data_out_clear;
+  logic                 mux_sel_err;
+  logic                 sp_enc_err;
+  logic                 rnd_ctr_err;
+  logic                 op_err;
+  logic                 prng_reseed_ack;
+  logic                 sub_bytes_out_req_n;
+  logic                 key_expand_out_req_n;
+  logic [3:0]           rnd_ctr_q;
+  logic [3:0]           rnd_ctr_rem_q;
+  logic [3:0]           num_rounds_q;
+  logic                 crypt_q_n;
+  logic                 dec_key_gen_q_n;
+  logic                 prng_reseed_q;
+  logic                 key_clear_q;
+  logic                 data_out_clear_q;
 
   assign {in_valid_n,
           out_ready_n,
@@ -193,6 +197,7 @@
           mux_sel_err,
           sp_enc_err,
           rnd_ctr_err,
+          op_err,
           prng_reseed_ack,
           sub_bytes_out_req_n,
           key_expand_out_req_n,
@@ -268,6 +273,7 @@
     .mux_sel_err_i         ( mux_sel_err           ),
     .sp_enc_err_i          ( sp_enc_err            ),
     .rnd_ctr_err_i         ( rnd_ctr_err           ),
+    .op_err_i              ( op_err                ),
     .alert_o               ( alert                 ),
 
     .prng_update_o         ( prng_update           ),
diff --git a/hw/ip/aes/rtl/aes_cipher_control_fsm_p.sv b/hw/ip/aes/rtl/aes_cipher_control_fsm_p.sv
index adcb9ae..a31c2eb 100644
--- a/hw/ip/aes/rtl/aes_cipher_control_fsm_p.sv
+++ b/hw/ip/aes/rtl/aes_cipher_control_fsm_p.sv
@@ -35,6 +35,7 @@
   input  logic             mux_sel_err_i,
   input  logic             sp_enc_err_i,
   input  logic             rnd_ctr_err_i,
+  input  logic             op_err_i,
   output logic             alert_o,
 
   // Control signals for masking PRNG
@@ -99,6 +100,7 @@
     mux_sel_err_i,
     sp_enc_err_i,
     rnd_ctr_err_i,
+    op_err_i,
     prng_reseed_ack_i,
     sub_bytes_out_req_i,
     key_expand_out_req_i,
@@ -128,6 +130,7 @@
     mux_sel_err_i,
     sp_enc_err_i,
     rnd_ctr_err_i,
+    op_err_i,
     prng_reseed_ack_i,
     sub_bytes_out_req_i,
     key_expand_out_req_i,
@@ -150,31 +153,32 @@
     .out_o(in_buf)
   );
 
-  logic             in_valid;
-  logic             out_ready;
-  logic             cfg_valid;
-  logic             op_raw;
-  ciph_op_e         op;
-  key_len_e         key_len;
-  logic             crypt;
-  logic             dec_key_gen;
-  logic             prng_reseed;
-  logic             key_clear;
-  logic             data_out_clear;
-  logic             mux_sel_err;
-  logic             sp_enc_err;
-  logic             rnd_ctr_err;
-  logic             prng_reseed_ack;
-  logic             sub_bytes_out_req;
-  logic             key_expand_out_req;
-  logic [3:0]       rnd_ctr_q;
-  logic [3:0]       rnd_ctr_rem_q;
-  logic [3:0]       num_rounds_q;
-  logic             crypt_q;
-  logic             dec_key_gen_q;
-  logic             prng_reseed_q;
-  logic             key_clear_q;
-  logic             data_out_clear_q;
+  logic                 in_valid;
+  logic                 out_ready;
+  logic                 cfg_valid;
+  ciph_op_e             op;
+  logic [$bits(op)-1:0] op_raw;
+  key_len_e             key_len;
+  logic                 crypt;
+  logic                 dec_key_gen;
+  logic                 prng_reseed;
+  logic                 key_clear;
+  logic                 data_out_clear;
+  logic                 mux_sel_err;
+  logic                 sp_enc_err;
+  logic                 rnd_ctr_err;
+  logic                 op_err;
+  logic                 prng_reseed_ack;
+  logic                 sub_bytes_out_req;
+  logic                 key_expand_out_req;
+  logic [3:0]           rnd_ctr_q;
+  logic [3:0]           rnd_ctr_rem_q;
+  logic [3:0]           num_rounds_q;
+  logic                 crypt_q;
+  logic                 dec_key_gen_q;
+  logic                 prng_reseed_q;
+  logic                 key_clear_q;
+  logic                 data_out_clear_q;
 
   assign {in_valid,
           out_ready,
@@ -189,6 +193,7 @@
           mux_sel_err,
           sp_enc_err,
           rnd_ctr_err,
+          op_err,
           prng_reseed_ack,
           sub_bytes_out_req,
           key_expand_out_req,
@@ -260,6 +265,7 @@
     .mux_sel_err_i         ( mux_sel_err            ),
     .sp_enc_err_i          ( sp_enc_err             ),
     .rnd_ctr_err_i         ( rnd_ctr_err            ),
+    .op_err_i              ( op_err                 ),
     .alert_o               ( alert                  ),
 
     .prng_update_o         ( prng_update            ),
diff --git a/hw/ip/aes/rtl/aes_cipher_core.sv b/hw/ip/aes/rtl/aes_cipher_core.sv
index 759b2e0..ca031d1 100644
--- a/hw/ip/aes/rtl/aes_cipher_core.sv
+++ b/hw/ip/aes/rtl/aes_cipher_core.sv
@@ -159,6 +159,7 @@
   state_sel_e                         state_sel;
   logic                               state_sel_err;
 
+  ciph_op_e                           op;
   sp2v_e                              sub_bytes_en;
   sp2v_e                              sub_bytes_out_req;
   sp2v_e                              sub_bytes_out_ack;
@@ -213,8 +214,10 @@
   round_key_sel_e                     round_key_sel;
   logic                               round_key_sel_err;
 
+  logic                               cfg_valid;
   logic                               mux_sel_err;
   logic                               sp_enc_err_d, sp_enc_err_q;
+  logic                               op_err;
 
   // Pseudo-random data for clearing and masking purposes
   logic                       [127:0] prd_clearing_128 [NumShares];
@@ -238,6 +241,21 @@
     end
   end
 
+  // op_i is one-hot encoded. Check the provided value and use the checked version internally.
+
+  // This primitive is used to place a size-only constraint on the
+  // buffers to act as a synthesis optimization barrier.
+  logic [$bits(ciph_op_e)-1:0] op_raw;
+  prim_buf #(
+    .Width($bits(ciph_op_e))
+  ) u_prim_buf_op (
+    .in_i(op_i),
+    .out_o(op_raw)
+  );
+  assign op        = ciph_op_e'(op_raw);
+  assign op_err    = ~(op == CIPH_FWD || op == CIPH_INV);
+  assign cfg_valid = cfg_valid_i & ~op_err;
+
   //////////
   // Data //
   //////////
@@ -356,7 +374,7 @@
     .en_i      ( sub_bytes_en      ),
     .out_req_o ( sub_bytes_out_req ),
     .out_ack_i ( sub_bytes_out_ack ),
-    .op_i      ( op_i              ),
+    .op_i      ( op                ),
     .data_i    ( state_q[0]        ),
     .mask_i    ( sb_in_mask        ),
     .prd_i     ( prd_sub_bytes     ),
@@ -375,13 +393,13 @@
     end
 
     aes_shift_rows u_aes_shift_rows (
-      .op_i   ( op_i              ),
+      .op_i   ( op                ),
       .data_i ( shift_rows_in[s]  ),
       .data_o ( shift_rows_out[s] )
     );
 
     aes_mix_columns u_aes_mix_columns (
-      .op_i   ( op_i               ),
+      .op_i   ( op                 ),
       .data_i ( shift_rows_out[s]  ),
       .data_o ( mix_columns_out[s] )
     );
@@ -448,7 +466,7 @@
   ) u_aes_key_expand (
     .clk_i       ( clk_i              ),
     .rst_ni      ( rst_ni             ),
-    .cfg_valid_i ( cfg_valid_i        ),
+    .cfg_valid_i ( cfg_valid          ),
     .op_i        ( key_expand_op      ),
     .en_i        ( key_expand_en      ),
     .out_req_o   ( key_expand_out_req ),
@@ -509,8 +527,8 @@
     .out_valid_o          ( out_valid_o         ),
     .out_ready_i          ( out_ready_i         ),
 
-    .cfg_valid_i          ( cfg_valid_i         ),
-    .op_i                 ( op_i                ),
+    .cfg_valid_i          ( cfg_valid           ),
+    .op_i                 ( op                  ),
     .key_len_i            ( key_len_i           ),
     .crypt_i              ( crypt_i             ),
     .crypt_o              ( crypt_o             ),
@@ -524,6 +542,7 @@
     .data_out_clear_o     ( data_out_clear_o    ),
     .mux_sel_err_i        ( mux_sel_err         ),
     .sp_enc_err_i         ( sp_enc_err_q        ),
+    .op_err_i             ( op_err              ),
     .alert_o              ( alert_o             ),
 
     .prng_update_o        ( prd_masking_upd     ),
diff --git a/hw/ip/aes/rtl/aes_control_fsm.sv b/hw/ip/aes/rtl/aes_control_fsm.sv
index 622b58a..a680119 100644
--- a/hw/ip/aes/rtl/aes_control_fsm.sv
+++ b/hw/ip/aes/rtl/aes_control_fsm.sv
@@ -171,6 +171,7 @@
 
   logic                     cfg_valid;
   logic                     no_alert;
+  logic                     cipher_op_err;
   logic                     start_common, start_ecb, start_cbc, start_cfb, start_ofb, start_ctr;
   logic                     start;
   logic                     finish;
@@ -212,6 +213,9 @@
   assign cfg_valid = ~((mode_i == AES_NONE) | ctrl_err_storage_i);
   assign no_alert  = ~alert_fatal_i;
 
+  // cipher_op_i is obtained from the configuration of the control register with additional logic.
+  assign cipher_op_err = ~(cipher_op_i == CIPH_FWD || cipher_op_i == CIPH_INV);
+
   // Check common start conditions. These are needed for any mode, unless we are running in
   // manual mode.
   assign start_common = key_init_ready & data_in_new &
@@ -520,7 +524,8 @@
           // let data propagate in case of mux selector or sparsely encoded signals taking on
           // invalid values.
           cipher_out_ready_o = finish;
-          cipher_out_done    = finish & cipher_out_valid_i & ~mux_sel_err_i & ~sp_enc_err_i;
+          cipher_out_done    = finish & cipher_out_valid_i &
+              ~mux_sel_err_i & ~sp_enc_err_i & ~cipher_op_err;
 
           // Signal if the cipher core is stalled (because previous output has not yet been read).
           stall    = ~finish & cipher_out_valid_i;
@@ -602,7 +607,7 @@
           if (cipher_data_out_clear_i) begin
             // Clear output data and the trigger bit. Don't release data from cipher core in case
             // of mux selector or sparsely encoded signals taking on invalid values.
-            data_out_we_o     = ~mux_sel_err_i & ~sp_enc_err_i;
+            data_out_we_o     = ~mux_sel_err_i & ~sp_enc_err_i & ~cipher_op_err;
             data_out_clear_we = 1'b1;
           end
 
@@ -623,7 +628,8 @@
 
     // Unconditionally jump into the terminal error state in case a mux selector or a sparsely
     // encoded signal becomes invalid, or if the life cycle controller triggers an escalation.
-    if (mux_sel_err_i || sp_enc_err_i || lc_escalate_en_i != lc_ctrl_pkg::Off) begin
+    if (mux_sel_err_i || sp_enc_err_i || cipher_op_err ||
+            lc_escalate_en_i != lc_ctrl_pkg::Off) begin
       aes_ctrl_ns = ERROR;
     end
   end
@@ -856,8 +862,14 @@
       AES_CTR,
       AES_NONE
       })
-  `ASSERT_KNOWN(AesOpKnown, op_i)
-  `ASSERT_KNOWN(AesCiphOpKnown, cipher_op_i)
+  `ASSERT(AesOpValid, !ctrl_err_storage_i |-> op_i inside {
+      AES_ENC,
+      AES_DEC
+      })
+  `ASSERT(AesCiphOpValid, !cipher_op_err |-> cipher_op_i inside {
+      CIPH_FWD,
+      CIPH_INV
+      })
   `ASSERT(AesControlStateValid, !alert_o |-> aes_ctrl_cs inside {
       IDLE,
       LOAD,
diff --git a/hw/ip/aes/rtl/aes_control_fsm_n.sv b/hw/ip/aes/rtl/aes_control_fsm_n.sv
index 48db140..8e7fbf7 100644
--- a/hw/ip/aes/rtl/aes_control_fsm_n.sv
+++ b/hw/ip/aes/rtl/aes_control_fsm_n.sv
@@ -211,8 +211,8 @@
   logic                                    ctrl_err_storage;
   aes_op_e                                 op;
   aes_mode_e                               mode;
-  logic                                    cipher_op_raw;
   ciph_op_e                                cipher_op;
+  logic             [$bits(cipher_op)-1:0] cipher_op_raw;
   logic                                    sideload;
   prs_rate_e                               prng_reseed_rate;
   logic                                    manual_operation;
diff --git a/hw/ip/aes/rtl/aes_control_fsm_p.sv b/hw/ip/aes/rtl/aes_control_fsm_p.sv
index cff606f..3a93540 100644
--- a/hw/ip/aes/rtl/aes_control_fsm_p.sv
+++ b/hw/ip/aes/rtl/aes_control_fsm_p.sv
@@ -207,8 +207,8 @@
   logic                                    ctrl_err_storage;
   aes_op_e                                 op;
   aes_mode_e                               mode;
-  logic                                    cipher_op_raw;
   ciph_op_e                                cipher_op;
+  logic             [$bits(cipher_op)-1:0] cipher_op_raw;
   logic                                    sideload;
   prs_rate_e                               prng_reseed_rate;
   logic                                    manual_operation;
diff --git a/hw/ip/aes/rtl/aes_core.sv b/hw/ip/aes/rtl/aes_core.sv
index 624e2cf..671e0d4 100644
--- a/hw/ip/aes/rtl/aes_core.sv
+++ b/hw/ip/aes/rtl/aes_core.sv
@@ -872,7 +872,10 @@
       AES_CTR,
       AES_NONE
       })
-  `ASSERT_KNOWN(AesOpKnown, aes_op_q)
+  `ASSERT(AesOpValid, !ctrl_err_storage |-> aes_op_q inside {
+      AES_ENC,
+      AES_DEC
+      })
 
   // Check parameters
   `ASSERT_INIT(AesNumSlicesCtr, NumSlicesCtr == 8)
diff --git a/hw/ip/aes/rtl/aes_ctrl_reg_shadowed.sv b/hw/ip/aes/rtl/aes_ctrl_reg_shadowed.sv
index 9dafc81..700838b 100644
--- a/hw/ip/aes/rtl/aes_ctrl_reg_shadowed.sv
+++ b/hw/ip/aes/rtl/aes_ctrl_reg_shadowed.sv
@@ -45,6 +45,7 @@
 
   // Signals
   ctrl_reg_t ctrl_wd;
+  aes_op_e   op;
   aes_mode_e mode;
   key_len_e  key_len;
   prs_rate_e prng_reseed_rate;
@@ -73,7 +74,14 @@
       reg2hw_ctrl_i.force_zero_masks.qe;
 
   // Get and resolve values from register interface.
-  assign ctrl_wd.operation = aes_op_e'(reg2hw_ctrl_i.operation.q);
+  assign op = aes_op_e'(reg2hw_ctrl_i.operation.q);
+  always_comb begin : op_get
+    unique case (op)
+      AES_ENC: ctrl_wd.operation = AES_ENC;
+      AES_DEC: ctrl_wd.operation = AES_DEC;
+      default: ctrl_wd.operation = AES_ENC; // unsupported values are mapped to AES_ENC
+    endcase
+  end
 
   assign mode = aes_mode_e'(reg2hw_ctrl_i.mode.q);
   always_comb begin : mode_get
@@ -128,7 +136,7 @@
     .rst_shadowed_ni,
     .re         (reg2hw_ctrl_i.operation.re),
     .we         (we_i),
-    .wd         (ctrl_wd.operation),
+    .wd         ({ctrl_wd.operation}),
     .de         (1'b0),
     .d          ('0),
     .qe         (),
diff --git a/hw/ip/aes/rtl/aes_key_expand.sv b/hw/ip/aes/rtl/aes_key_expand.sv
index cb828e0..93ce733 100644
--- a/hw/ip/aes/rtl/aes_key_expand.sv
+++ b/hw/ip/aes/rtl/aes_key_expand.sv
@@ -447,7 +447,10 @@
        SBoxImpl == SBoxImplCanright)))
 
   // Selectors must be known/valid
-  `ASSERT_KNOWN(AesCiphOpKnown, op_i)
+  `ASSERT(AesCiphOpValid, cfg_valid_i |-> op_i inside {
+      CIPH_FWD,
+      CIPH_INV
+      })
   `ASSERT(AesKeyLenValid, cfg_valid_i |-> key_len_i inside {
       AES_128,
       AES_192,
diff --git a/hw/ip/aes/rtl/aes_mix_single_column.sv b/hw/ip/aes/rtl/aes_mix_single_column.sv
index acdc9c0..28e5346 100644
--- a/hw/ip/aes/rtl/aes_mix_single_column.sv
+++ b/hw/ip/aes/rtl/aes_mix_single_column.sv
@@ -56,8 +56,10 @@
   assign z[1] = y2 ^ y[1];
 
   // Mux z
-  assign z_muxed[0] = (op_i == CIPH_FWD) ? 8'b0 : z[0];
-  assign z_muxed[1] = (op_i == CIPH_FWD) ? 8'b0 : z[1];
+  assign z_muxed[0] = (op_i == CIPH_FWD) ? 8'b0 :
+                      (op_i == CIPH_INV) ? z[0] : 8'b0;
+  assign z_muxed[1] = (op_i == CIPH_FWD) ? 8'b0 :
+                      (op_i == CIPH_INV) ? z[1] : 8'b0;
 
   // Drive outputs
   assign data_o[0] = data_i[1] ^ x_mul2[3] ^ x[1] ^ z_muxed[1];
diff --git a/hw/ip/aes/rtl/aes_pkg.sv b/hw/ip/aes/rtl/aes_pkg.sv
index 0b9bb2c..a9c2b25 100644
--- a/hw/ip/aes/rtl/aes_pkg.sv
+++ b/hw/ip/aes/rtl/aes_pkg.sv
@@ -77,13 +77,14 @@
 
 
 // Parameters used for controlgroups in the coverage
+parameter int AES_OP_WIDTH             = 2;
 parameter int AES_MODE_WIDTH           = 6;
 parameter int AES_KEYLEN_WIDTH         = 3;
 parameter int AES_PRNGRESEEDRATE_WIDTH = 3;
 
-typedef enum logic {
-  AES_ENC = 1'b0,
-  AES_DEC = 1'b1
+typedef enum logic [AES_OP_WIDTH-1:0] {
+  AES_ENC = 2'b01,
+  AES_DEC = 2'b10
 } aes_op_e;
 
 typedef enum logic [AES_MODE_WIDTH-1:0] {
@@ -95,9 +96,9 @@
   AES_NONE = 6'b10_0000
 } aes_mode_e;
 
-typedef enum logic {
-  CIPH_FWD = 1'b0,
-  CIPH_INV = 1'b1
+typedef enum logic [AES_OP_WIDTH-1:0] {
+  CIPH_FWD = 2'b01,
+  CIPH_INV = 2'b10
 } ciph_op_e;
 
 typedef enum logic [AES_KEYLEN_WIDTH-1:0] {
diff --git a/hw/ip/aes/rtl/aes_reg_pkg.sv b/hw/ip/aes/rtl/aes_reg_pkg.sv
index d09e07e..4eaddaf 100644
--- a/hw/ip/aes/rtl/aes_reg_pkg.sv
+++ b/hw/ip/aes/rtl/aes_reg_pkg.sv
@@ -57,7 +57,7 @@
 
   typedef struct packed {
     struct packed {
-      logic        q;
+      logic [1:0]  q;
       logic        qe;
       logic        re;
     } operation;
@@ -143,7 +143,7 @@
 
   typedef struct packed {
     struct packed {
-      logic        d;
+      logic [1:0]  d;
     } operation;
     struct packed {
       logic [5:0]  d;
@@ -217,13 +217,13 @@
 
   // Register -> HW type
   typedef struct packed {
-    aes_reg2hw_alert_test_reg_t alert_test; // [956:953]
-    aes_reg2hw_key_share0_mreg_t [7:0] key_share0; // [952:689]
-    aes_reg2hw_key_share1_mreg_t [7:0] key_share1; // [688:425]
-    aes_reg2hw_iv_mreg_t [3:0] iv; // [424:293]
-    aes_reg2hw_data_in_mreg_t [3:0] data_in; // [292:161]
-    aes_reg2hw_data_out_mreg_t [3:0] data_out; // [160:29]
-    aes_reg2hw_ctrl_shadowed_reg_t ctrl_shadowed; // [28:6]
+    aes_reg2hw_alert_test_reg_t alert_test; // [957:954]
+    aes_reg2hw_key_share0_mreg_t [7:0] key_share0; // [953:690]
+    aes_reg2hw_key_share1_mreg_t [7:0] key_share1; // [689:426]
+    aes_reg2hw_iv_mreg_t [3:0] iv; // [425:294]
+    aes_reg2hw_data_in_mreg_t [3:0] data_in; // [293:162]
+    aes_reg2hw_data_out_mreg_t [3:0] data_out; // [161:30]
+    aes_reg2hw_ctrl_shadowed_reg_t ctrl_shadowed; // [29:6]
     aes_reg2hw_ctrl_aux_shadowed_reg_t ctrl_aux_shadowed; // [5:5]
     aes_reg2hw_trigger_reg_t trigger; // [4:1]
     aes_reg2hw_status_reg_t status; // [0:0]
@@ -231,12 +231,12 @@
 
   // HW -> register type
   typedef struct packed {
-    aes_hw2reg_key_share0_mreg_t [7:0] key_share0; // [937:682]
-    aes_hw2reg_key_share1_mreg_t [7:0] key_share1; // [681:426]
-    aes_hw2reg_iv_mreg_t [3:0] iv; // [425:298]
-    aes_hw2reg_data_in_mreg_t [3:0] data_in; // [297:166]
-    aes_hw2reg_data_out_mreg_t [3:0] data_out; // [165:38]
-    aes_hw2reg_ctrl_shadowed_reg_t ctrl_shadowed; // [37:22]
+    aes_hw2reg_key_share0_mreg_t [7:0] key_share0; // [938:683]
+    aes_hw2reg_key_share1_mreg_t [7:0] key_share1; // [682:427]
+    aes_hw2reg_iv_mreg_t [3:0] iv; // [426:299]
+    aes_hw2reg_data_in_mreg_t [3:0] data_in; // [298:167]
+    aes_hw2reg_data_out_mreg_t [3:0] data_out; // [166:39]
+    aes_hw2reg_ctrl_shadowed_reg_t ctrl_shadowed; // [38:22]
     aes_hw2reg_trigger_reg_t trigger; // [21:14]
     aes_hw2reg_status_reg_t status; // [13:0]
   } aes_hw2reg_t;
@@ -329,8 +329,8 @@
   parameter logic [31:0] AES_DATA_OUT_2_DATA_OUT_2_RESVAL = 32'h 0;
   parameter logic [31:0] AES_DATA_OUT_3_RESVAL = 32'h 0;
   parameter logic [31:0] AES_DATA_OUT_3_DATA_OUT_3_RESVAL = 32'h 0;
-  parameter logic [15:0] AES_CTRL_SHADOWED_RESVAL = 16'h 8c0;
-  parameter logic [0:0] AES_CTRL_SHADOWED_OPERATION_RESVAL = 1'h 0;
+  parameter logic [16:0] AES_CTRL_SHADOWED_RESVAL = 17'h 1181;
+  parameter logic [1:0] AES_CTRL_SHADOWED_OPERATION_RESVAL = 2'h 1;
   parameter logic [5:0] AES_CTRL_SHADOWED_MODE_RESVAL = 6'h 20;
   parameter logic [2:0] AES_CTRL_SHADOWED_KEY_LEN_RESVAL = 3'h 1;
   parameter logic [0:0] AES_CTRL_SHADOWED_SIDELOAD_RESVAL = 1'h 0;
@@ -407,7 +407,7 @@
     4'b 1111, // index[26] AES_DATA_OUT_1
     4'b 1111, // index[27] AES_DATA_OUT_2
     4'b 1111, // index[28] AES_DATA_OUT_3
-    4'b 0011, // index[29] AES_CTRL_SHADOWED
+    4'b 0111, // index[29] AES_CTRL_SHADOWED
     4'b 0001, // index[30] AES_CTRL_AUX_SHADOWED
     4'b 0001, // index[31] AES_CTRL_AUX_REGWEN
     4'b 0001, // index[32] AES_TRIGGER
diff --git a/hw/ip/aes/rtl/aes_reg_top.sv b/hw/ip/aes/rtl/aes_reg_top.sv
index 4e30344..7f048c8 100644
--- a/hw/ip/aes/rtl/aes_reg_top.sv
+++ b/hw/ip/aes/rtl/aes_reg_top.sv
@@ -170,8 +170,8 @@
   logic [31:0] data_out_3_qs;
   logic ctrl_shadowed_re;
   logic ctrl_shadowed_we;
-  logic ctrl_shadowed_operation_qs;
-  logic ctrl_shadowed_operation_wd;
+  logic [1:0] ctrl_shadowed_operation_qs;
+  logic [1:0] ctrl_shadowed_operation_wd;
   logic [5:0] ctrl_shadowed_mode_qs;
   logic [5:0] ctrl_shadowed_mode_wd;
   logic [2:0] ctrl_shadowed_key_len_qs;
@@ -728,9 +728,9 @@
 
 
   // R[ctrl_shadowed]: V(True)
-  //   F[operation]: 0:0
+  //   F[operation]: 1:0
   prim_subreg_ext #(
-    .DW    (1)
+    .DW    (2)
   ) u_ctrl_shadowed_operation (
     .re     (ctrl_shadowed_re),
     .we     (ctrl_shadowed_we),
@@ -742,7 +742,7 @@
     .qs     (ctrl_shadowed_operation_qs)
   );
 
-  //   F[mode]: 6:1
+  //   F[mode]: 7:2
   prim_subreg_ext #(
     .DW    (6)
   ) u_ctrl_shadowed_mode (
@@ -756,7 +756,7 @@
     .qs     (ctrl_shadowed_mode_qs)
   );
 
-  //   F[key_len]: 9:7
+  //   F[key_len]: 10:8
   prim_subreg_ext #(
     .DW    (3)
   ) u_ctrl_shadowed_key_len (
@@ -770,7 +770,7 @@
     .qs     (ctrl_shadowed_key_len_qs)
   );
 
-  //   F[sideload]: 10:10
+  //   F[sideload]: 11:11
   prim_subreg_ext #(
     .DW    (1)
   ) u_ctrl_shadowed_sideload (
@@ -784,7 +784,7 @@
     .qs     (ctrl_shadowed_sideload_qs)
   );
 
-  //   F[prng_reseed_rate]: 13:11
+  //   F[prng_reseed_rate]: 14:12
   prim_subreg_ext #(
     .DW    (3)
   ) u_ctrl_shadowed_prng_reseed_rate (
@@ -798,7 +798,7 @@
     .qs     (ctrl_shadowed_prng_reseed_rate_qs)
   );
 
-  //   F[manual_operation]: 14:14
+  //   F[manual_operation]: 15:15
   prim_subreg_ext #(
     .DW    (1)
   ) u_ctrl_shadowed_manual_operation (
@@ -812,7 +812,7 @@
     .qs     (ctrl_shadowed_manual_operation_qs)
   );
 
-  //   F[force_zero_masks]: 15:15
+  //   F[force_zero_masks]: 16:16
   prim_subreg_ext #(
     .DW    (1)
   ) u_ctrl_shadowed_force_zero_masks (
@@ -1328,19 +1328,19 @@
   assign ctrl_shadowed_re = addr_hit[29] & reg_re & !reg_error;
   assign ctrl_shadowed_we = addr_hit[29] & reg_we & !reg_error;
 
-  assign ctrl_shadowed_operation_wd = reg_wdata[0];
+  assign ctrl_shadowed_operation_wd = reg_wdata[1:0];
 
-  assign ctrl_shadowed_mode_wd = reg_wdata[6:1];
+  assign ctrl_shadowed_mode_wd = reg_wdata[7:2];
 
-  assign ctrl_shadowed_key_len_wd = reg_wdata[9:7];
+  assign ctrl_shadowed_key_len_wd = reg_wdata[10:8];
 
-  assign ctrl_shadowed_sideload_wd = reg_wdata[10];
+  assign ctrl_shadowed_sideload_wd = reg_wdata[11];
 
-  assign ctrl_shadowed_prng_reseed_rate_wd = reg_wdata[13:11];
+  assign ctrl_shadowed_prng_reseed_rate_wd = reg_wdata[14:12];
 
-  assign ctrl_shadowed_manual_operation_wd = reg_wdata[14];
+  assign ctrl_shadowed_manual_operation_wd = reg_wdata[15];
 
-  assign ctrl_shadowed_force_zero_masks_wd = reg_wdata[15];
+  assign ctrl_shadowed_force_zero_masks_wd = reg_wdata[16];
   assign ctrl_aux_shadowed_re = addr_hit[30] & reg_re & !reg_error;
   assign ctrl_aux_shadowed_we = addr_hit[30] & reg_we & !reg_error;
 
@@ -1480,13 +1480,13 @@
       end
 
       addr_hit[29]: begin
-        reg_rdata_next[0] = ctrl_shadowed_operation_qs;
-        reg_rdata_next[6:1] = ctrl_shadowed_mode_qs;
-        reg_rdata_next[9:7] = ctrl_shadowed_key_len_qs;
-        reg_rdata_next[10] = ctrl_shadowed_sideload_qs;
-        reg_rdata_next[13:11] = ctrl_shadowed_prng_reseed_rate_qs;
-        reg_rdata_next[14] = ctrl_shadowed_manual_operation_qs;
-        reg_rdata_next[15] = ctrl_shadowed_force_zero_masks_qs;
+        reg_rdata_next[1:0] = ctrl_shadowed_operation_qs;
+        reg_rdata_next[7:2] = ctrl_shadowed_mode_qs;
+        reg_rdata_next[10:8] = ctrl_shadowed_key_len_qs;
+        reg_rdata_next[11] = ctrl_shadowed_sideload_qs;
+        reg_rdata_next[14:12] = ctrl_shadowed_prng_reseed_rate_qs;
+        reg_rdata_next[15] = ctrl_shadowed_manual_operation_qs;
+        reg_rdata_next[16] = ctrl_shadowed_force_zero_masks_qs;
       end
 
       addr_hit[30]: begin
diff --git a/hw/ip/aes/rtl/aes_sbox_canright.sv b/hw/ip/aes/rtl/aes_sbox_canright.sv
index ff4da73..4d7432d 100644
--- a/hw/ip/aes/rtl/aes_sbox_canright.sv
+++ b/hw/ip/aes/rtl/aes_sbox_canright.sv
@@ -55,14 +55,16 @@
   logic [7:0] data_basis_x, data_inverse;
 
   // Convert to normal basis X.
-  assign data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X) :
-                                             aes_mvm(data_i ^ 8'h63, S2X);
+  assign data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X)         :
+                        (op_i == CIPH_INV) ? aes_mvm(data_i ^ 8'h63, S2X) :
+                                             aes_mvm(data_i, A2X);
 
   // Do the inversion in normal basis X.
   assign data_inverse = aes_inverse_gf2p8(data_basis_x);
 
   // Convert to basis S or A.
   assign data_o       = (op_i == CIPH_FWD) ? aes_mvm(data_inverse, X2S) ^ 8'h63 :
-                                             aes_mvm(data_inverse, X2A);
+                        (op_i == CIPH_INV) ? aes_mvm(data_inverse, X2A) :
+                                             aes_mvm(data_inverse, X2S) ^ 8'h63;
 
 endmodule
diff --git a/hw/ip/aes/rtl/aes_sbox_canright_masked.sv b/hw/ip/aes/rtl/aes_sbox_canright_masked.sv
index db93d14..b537068 100644
--- a/hw/ip/aes/rtl/aes_sbox_canright_masked.sv
+++ b/hw/ip/aes/rtl/aes_sbox_canright_masked.sv
@@ -450,8 +450,9 @@
   logic [7:0] in_mask_basis_x, out_mask_basis_x;
 
   // Convert data to normal basis X.
-  assign in_data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X) :
-                                                aes_mvm(data_i ^ 8'h63, S2X);
+  assign in_data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X)         :
+                           (op_i == CIPH_INV) ? aes_mvm(data_i ^ 8'h63, S2X) :
+                                                aes_mvm(data_i, A2X);
 
   // For the masked Canright SBox, the output mask directly corresponds to the pseduo-random data
   // provided as input.
@@ -460,10 +461,12 @@
   // Convert masks to normal basis X.
   // The addition of constant 8'h63 following the affine transformation is skipped.
   assign in_mask_basis_x  = (op_i == CIPH_FWD) ? aes_mvm(mask_i, A2X) :
-                                                 aes_mvm(mask_i, S2X);
+                            (op_i == CIPH_INV) ? aes_mvm(mask_i, S2X) :
+                                                 aes_mvm(mask_i, A2X);
 
   // The output mask is converted in the opposite direction.
   assign out_mask_basis_x = (op_i == CIPH_INV) ? aes_mvm(mask_o, A2X) :
+                            (op_i == CIPH_FWD) ? aes_mvm(mask_o, S2X) :
                                                  aes_mvm(mask_o, S2X);
 
   // Do the inversion in normal basis X.
@@ -476,6 +479,7 @@
 
   // Convert to basis S or A.
   assign data_o = (op_i == CIPH_FWD) ? (aes_mvm(out_data_basis_x, X2S) ^ 8'h63) :
-                                       (aes_mvm(out_data_basis_x, X2A));
+                  (op_i == CIPH_INV) ? (aes_mvm(out_data_basis_x, X2A))         :
+                                       (aes_mvm(out_data_basis_x, X2S) ^ 8'h63);
 
 endmodule
diff --git a/hw/ip/aes/rtl/aes_sbox_canright_masked_noreuse.sv b/hw/ip/aes/rtl/aes_sbox_canright_masked_noreuse.sv
index 20b2f02..c7577b4 100644
--- a/hw/ip/aes/rtl/aes_sbox_canright_masked_noreuse.sv
+++ b/hw/ip/aes/rtl/aes_sbox_canright_masked_noreuse.sv
@@ -409,8 +409,9 @@
   logic [7:0] in_mask_basis_x, out_mask_basis_x;
 
   // Convert data to normal basis X.
-  assign in_data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X) :
-                                                aes_mvm(data_i ^ 8'h63, S2X);
+  assign in_data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X)         :
+                           (op_i == CIPH_INV) ? aes_mvm(data_i ^ 8'h63, S2X) :
+                                                aes_mvm(data_i, A2X);
 
   // For the masked Canright SBox with no re-use, the output mask directly corresponds to the
   // LSBs of the pseduo-random data provided as input.
@@ -423,10 +424,12 @@
   // Convert masks to normal basis X.
   // The addition of constant 8'h63 following the affine transformation is skipped.
   assign in_mask_basis_x  = (op_i == CIPH_FWD) ? aes_mvm(mask_i, A2X) :
-                                                 aes_mvm(mask_i, S2X);
+                            (op_i == CIPH_INV) ? aes_mvm(mask_i, S2X) :
+                                                 aes_mvm(mask_i, A2X);
 
   // The output mask is converted in the opposite direction.
   assign out_mask_basis_x = (op_i == CIPH_INV) ? aes_mvm(mask_o, A2X) :
+                            (op_i == CIPH_FWD) ? aes_mvm(mask_o, S2X) :
                                                  aes_mvm(mask_o, S2X);
 
   // Do the inversion in normal basis X.
@@ -440,6 +443,7 @@
 
   // Convert to basis S or A.
   assign data_o = (op_i == CIPH_FWD) ? (aes_mvm(out_data_basis_x, X2S) ^ 8'h63) :
-                                       (aes_mvm(out_data_basis_x, X2A));
+                  (op_i == CIPH_INV) ? (aes_mvm(out_data_basis_x, X2A))         :
+                                       (aes_mvm(out_data_basis_x, X2S) ^ 8'h63);
 
 endmodule
diff --git a/hw/ip/aes/rtl/aes_sbox_dom.sv b/hw/ip/aes/rtl/aes_sbox_dom.sv
index 6dfdde4..ef4bf68 100644
--- a/hw/ip/aes/rtl/aes_sbox_dom.sv
+++ b/hw/ip/aes/rtl/aes_sbox_dom.sv
@@ -1010,13 +1010,15 @@
   prd_out_t   out_prd;
 
   // Convert data to normal basis X.
-  assign in_data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X) :
-                                                aes_mvm(data_i ^ 8'h63, S2X);
+  assign in_data_basis_x = (op_i == CIPH_FWD) ? aes_mvm(data_i, A2X)         :
+                           (op_i == CIPH_INV) ? aes_mvm(data_i ^ 8'h63, S2X) :
+                                                aes_mvm(data_i, A2X);
 
   // Convert mask to normal basis X.
   // The addition of constant 8'h63 prior to the affine transformation is skipped.
-  assign in_mask_basis_x  = (op_i == CIPH_FWD) ? aes_mvm(mask_i, A2X) :
-                                                 aes_mvm(mask_i, S2X);
+  assign in_mask_basis_x = (op_i == CIPH_FWD) ? aes_mvm(mask_i, A2X) :
+                           (op_i == CIPH_INV) ? aes_mvm(mask_i, S2X) :
+                                                aes_mvm(mask_i, A2X);
 
   // Do the inversion in normal basis X.
   aes_dom_inverse_gf2p8 #(
@@ -1035,12 +1037,14 @@
 
   // Convert data to basis S or A.
   assign data_o = (op_i == CIPH_FWD) ? (aes_mvm(out_data_basis_x, X2S) ^ 8'h63) :
-                                       (aes_mvm(out_data_basis_x, X2A));
+                  (op_i == CIPH_INV) ? (aes_mvm(out_data_basis_x, X2A))         :
+                                       (aes_mvm(out_data_basis_x, X2S) ^ 8'h63);
 
   // Convert mask to basis S or A.
   // The addition of constant 8'h63 following the affine transformation is skipped.
   assign mask_o = (op_i == CIPH_FWD) ? aes_mvm(out_mask_basis_x, X2S) :
-                                       aes_mvm(out_mask_basis_x, X2A);
+                  (op_i == CIPH_INV) ? aes_mvm(out_mask_basis_x, X2A) :
+                                       aes_mvm(out_mask_basis_x, X2S);
 
   // Counter register
   logic [2:0] count_d, count_q;
diff --git a/hw/ip/aes/rtl/aes_sbox_lut.sv b/hw/ip/aes/rtl/aes_sbox_lut.sv
index 3eb9507..e57b910 100644
--- a/hw/ip/aes/rtl/aes_sbox_lut.sv
+++ b/hw/ip/aes/rtl/aes_sbox_lut.sv
@@ -114,6 +114,7 @@
   };
 
   // Drive output
-  assign data_o = (op_i == CIPH_FWD) ? SBOX_FWD[data_i] : SBOX_INV[data_i];
+  assign data_o = (op_i == CIPH_FWD) ? SBOX_FWD[data_i] :
+                  (op_i == CIPH_INV) ? SBOX_INV[data_i] : SBOX_FWD[data_i];
 
 endmodule
diff --git a/hw/ip/aes/rtl/aes_shift_rows.sv b/hw/ip/aes/rtl/aes_shift_rows.sv
index b33b570..e5a62e9 100644
--- a/hw/ip/aes/rtl/aes_shift_rows.sv
+++ b/hw/ip/aes/rtl/aes_shift_rows.sv
@@ -19,11 +19,13 @@
   assign data_o[2] = aes_circ_byte_shift(data_i[2], 2'h2);
 
   // Row 1
-  assign data_o[1] = (op_i == CIPH_FWD) ? aes_circ_byte_shift(data_i[1], 2'h3)
-                                        : aes_circ_byte_shift(data_i[1], 2'h1);
+  assign data_o[1] = (op_i == CIPH_FWD) ? aes_circ_byte_shift(data_i[1], 2'h3) :
+                     (op_i == CIPH_INV) ? aes_circ_byte_shift(data_i[1], 2'h1) :
+                                          aes_circ_byte_shift(data_i[1], 2'h3);
 
   // Row 3
-  assign data_o[3] = (op_i == CIPH_FWD) ? aes_circ_byte_shift(data_i[3], 2'h1)
-                                        : aes_circ_byte_shift(data_i[3], 2'h3);
+  assign data_o[3] = (op_i == CIPH_FWD) ? aes_circ_byte_shift(data_i[3], 2'h1) :
+                     (op_i == CIPH_INV) ? aes_circ_byte_shift(data_i[3], 2'h3) :
+                                          aes_circ_byte_shift(data_i[3], 2'h1);
 
 endmodule
diff --git a/hw/ip/aes/rtl/aes_wrap.sv b/hw/ip/aes/rtl/aes_wrap.sv
index 348f584..dfe4029 100644
--- a/hw/ip/aes/rtl/aes_wrap.sv
+++ b/hw/ip/aes/rtl/aes_wrap.sv
@@ -194,7 +194,7 @@
         h2d.a_valid   = 1'b1;
         h2d.a_opcode  = PutFullData;
         h2d.a_address = {{{32-BlockAw}{1'b0}}, AES_CTRL_SHADOWED_OFFSET};
-        h2d.a_data    = {19'h0, 1'b0 ,1'b0, SIDELOAD, AES_128, AES_MODE, AES_ENC};
+        h2d.a_data    = {18'h0, 1'b0 ,1'b0, SIDELOAD, AES_128, AES_MODE, AES_ENC};
 
         // We can't do back to back transactions. De-assert valid while receiving response.
         if (d2h.d_valid) begin
diff --git a/sw/device/lib/dif/dif_aes.c b/sw/device/lib/dif/dif_aes.c
index fc2b519..8666cc8 100644
--- a/sw/device/lib/dif/dif_aes.c
+++ b/sw/device/lib/dif/dif_aes.c
@@ -17,6 +17,18 @@
  */
 
 /*
+ * Field to select AES operation.
+ *
+ * Invalid input values, i.e., value with multiple bits set - are mapped to
+ * `kAesOperationFieldValEnc`.
+ */
+typedef enum aes_operation_field_val {
+  kAesOperationFieldValInvalid = 0x0,
+  kAesOperationFieldValEnc = 0x1, /**< Enrcyption. */
+  kAesOperationFieldValDec = 0x2, /**< Decryption. */
+} aes_operation_field_val_t;
+
+/*
  * Field to select AES block cipher mode.
  *
  * Invalid input values, i.e., value with multiple bits set - are mapped to
@@ -111,6 +123,38 @@
   }
 }
 
+static aes_operation_field_val_t operation_to_field(
+    dif_aes_operation_t operation) {
+  switch (operation) {
+    case kDifAesOperationEncrypt:
+      return kAesOperationFieldValEnc;
+    case kDifAesOperationDecrypt:
+      return kAesOperationFieldValDec;
+    default:
+      return kAesOperationFieldValInvalid;
+  }
+}
+
+// TODO: We should extract the MODE from the transaction using this function and
+// not separately provide it. See
+// https://github.com/lowRISC/opentitan/issues/10487
+// static aes_mode_field_val_t mode_to_field(dif_aes_mode_t mode) {
+//  switch (mode) {
+//    case kDifAesModeEcb:
+//      return kAesModeFieldValEcb;
+//    case kDifAesModeCbc:
+//      return kAesModeFieldValCbc;
+//    case kDifAesModeCfb:
+//      return kAesModeFieldValCfb;
+//    case kDifAesModeOfb:
+//      return kAesModeFieldValOfb;
+//    case kDifAesModeCtr:
+//      return kAesModeFieldValCtr;
+//    default:
+//      return kAesModeFieldValNone;
+//  }
+//}
+
 static aes_key_field_val_t key_to_field(dif_aes_key_length_t key) {
   switch (key) {
     case kDifAesKey128:
@@ -129,30 +173,37 @@
  *
  * @param aes AES state data.
  * @param transaction Configuration data, common across all Cipher modes.
- * @param cipher_mode_val Cipher Mode register write value.
+ * @param mode_val Block cipher mode of operation register write value.
  * @return `dif_result_t`.
  */
 static dif_result_t configure(const dif_aes_t *aes,
                               const dif_aes_transaction_t *transaction,
-                              aes_mode_field_val_t cipher_mode_val) {
+                              aes_mode_field_val_t mode_val) {
+  aes_operation_field_val_t operation_val =
+      operation_to_field(transaction->operation);
+  if (operation_val == kAesOperationFieldValInvalid) {
+    return kDifError;
+  }
+  // TODO: We should extract the mode from the transaction and not separately
+  // provide it. See https://github.com/lowRISC/opentitan/issues/10487
+  // aes_mode_field_val_t mode_val = mode_to_field(transaction->mode);
+  // if (mode_val == kAesModeFieldValNone) {
+  //  return kDifError;
+  //}
   aes_key_field_val_t key_len_val = key_to_field(transaction->key_len);
   if (key_len_val == kAesKeyFieldValInvalid) {
     return kDifError;
   }
 
-  uint32_t reg =
-      bitfield_field32_write(0, AES_CTRL_SHADOWED_KEY_LEN_FIELD, key_len_val);
+  uint32_t reg = bitfield_field32_write(0, AES_CTRL_SHADOWED_OPERATION_FIELD,
+                                        operation_val);
 
-  reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_MODE_FIELD,
-                               cipher_mode_val);
+  reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_MODE_FIELD, mode_val);
 
-  if (transaction->mode == kDifAesModeDecrypt) {
-    reg = bitfield_bit32_write(reg, AES_CTRL_SHADOWED_OPERATION_BIT, true);
-  } else {
-    reg = bitfield_bit32_write(reg, AES_CTRL_SHADOWED_OPERATION_BIT, false);
-  }
+  reg =
+      bitfield_field32_write(reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD, key_len_val);
 
-  if (transaction->operation == kDifAesOperationManual) {
+  if (transaction->manual_operation == kDifAesManualOperationManual) {
     reg =
         bitfield_bit32_write(reg, AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, true);
   } else {
@@ -197,16 +248,18 @@
 
   aes_clear_internal_state(aes);
 
-  uint32_t reg =
-      bitfield_bit32_write(0, AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, true);
-
   // Any values would do, illegal values chosen here.
+  uint32_t reg =
+      bitfield_field32_write(0, AES_CTRL_SHADOWED_OPERATION_FIELD, 0xffffffff);
+
   reg = bitfield_field32_write(reg, AES_CTRL_SHADOWED_MODE_FIELD,
                                AES_CTRL_SHADOWED_MODE_VALUE_AES_NONE);
 
   reg =
       bitfield_field32_write(reg, AES_CTRL_SHADOWED_KEY_LEN_FIELD, 0xffffffff);
 
+  reg = bitfield_bit32_write(reg, AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, true);
+
   aes_shadowed_write(aes->base_addr, AES_CTRL_SHADOWED_REG_OFFSET, reg);
 
   return kDifOk;
diff --git a/sw/device/lib/dif/dif_aes.h b/sw/device/lib/dif/dif_aes.h
index 65e767c..f75a7ce 100644
--- a/sw/device/lib/dif/dif_aes.h
+++ b/sw/device/lib/dif/dif_aes.h
@@ -82,6 +82,46 @@
 } dif_aes_data_t;
 
 /**
+ * AES operation.
+ */
+typedef enum dif_aes_operation {
+  /**
+   * AES encryption.
+   */
+  kDifAesOperationEncrypt = 0,
+  /**
+   * AES decryption.
+   */
+  kDifAesOperationDecrypt,
+} dif_aes_operation_t;
+
+/**
+ * AES block cipher mode of operation.
+ */
+typedef enum dif_aes_mode {
+  /**
+   * The Electronic Codebook Mode.
+   */
+  kDifAesModeEcb = 0,
+  /**
+   * The Cipher Block Chaining Mode.
+   */
+  kDifAesModeCbc,
+  /**
+   * The Cipher Feedback Mode.
+   */
+  kDifAesModeCfb,
+  /**
+   * The Output Feedback Mode.
+   */
+  kDifAesModeOfb,
+  /**
+   * The Counter Mode.
+   */
+  kDifAesModeCtr,
+} dif_aes_mode_t;
+
+/**
  * AES key length in bits.
  */
 typedef enum dif_aes_key_length {
@@ -100,34 +140,20 @@
 } dif_aes_key_length_t;
 
 /**
- * AES mode.
+ * AES manual operation.
  */
-typedef enum dif_aes_mode {
-  /**
-   * AES encryption mode.
-   */
-  kDifAesModeEncrypt = 0,
-  /**
-   * AES decryption mode.
-   */
-  kDifAesModeDecrypt,
-} dif_aes_mode_t;
-
-/**
- * AES operation.
- */
-typedef enum dif_aes_operation {
+typedef enum dif_aes_manual_operation {
   /**
    * AES operates in automatic mode - which means that the encryption/decryption
    * is automatically triggered on every successful `dif_aes_*_load_data()`.
    */
-  kDifAesOperationAuto = 0,
+  kDifAesManualOperationAuto = 0,
   /**
    * AES operates in manual mode - which means that the encryption/decryption
    * is manually triggered by `dif_aes_trigger(kDifAesTriggerStart)`.
    */
-  kDifAesOperationManual,
-} dif_aes_operation_t;
+  kDifAesManualOperationManual,
+} dif_aes_manual_operation_t;
 
 /**
  * AES masking.
@@ -151,9 +177,10 @@
  * Parameters for an AES transaction.
  */
 typedef struct dif_aes_transaction {
-  dif_aes_key_length_t key_len;
-  dif_aes_mode_t mode;
   dif_aes_operation_t operation;
+  dif_aes_mode_t mode;
+  dif_aes_key_length_t key_len;
+  dif_aes_manual_operation_t manual_operation;
   dif_aes_masking_t masking;
 } dif_aes_transaction_t;
 
diff --git a/sw/device/lib/dif/dif_aes_unittest.cc b/sw/device/lib/dif/dif_aes_unittest.cc
index 5b3a7ec..0c13bd2 100644
--- a/sw/device/lib/dif/dif_aes_unittest.cc
+++ b/sw/device/lib/dif/dif_aes_unittest.cc
@@ -60,9 +60,10 @@
   dif_aes_t aes_;
 
   const dif_aes_transaction_t kTransaction = {
+      .operation = kDifAesOperationEncrypt,
+      .mode = kDifAesModeEcb,
       .key_len = kDifAesKey128,
-      .mode = kDifAesModeEncrypt,
-      .operation = kDifAesOperationAuto,
+      .manual_operation = kDifAesManualOperationAuto,
       .masking = kDifAesMaskingInternalPrng,
   };
 
@@ -86,7 +87,7 @@
     EXPECT_WRITE32(AES_CTRL_SHADOWED_REG_OFFSET,
                    {{AES_CTRL_SHADOWED_KEY_LEN_OFFSET, 0x01},
                     {AES_CTRL_SHADOWED_MODE_OFFSET, 0x01},
-                    {AES_CTRL_SHADOWED_OPERATION_BIT, false},
+                    {AES_CTRL_SHADOWED_OPERATION_OFFSET, 0x1},
                     {AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, false},
                     {AES_CTRL_SHADOWED_FORCE_ZERO_MASKS_BIT, false}});
   }
@@ -105,7 +106,7 @@
     EXPECT_WRITE32(AES_CTRL_SHADOWED_REG_OFFSET,
                    {{AES_CTRL_SHADOWED_KEY_LEN_OFFSET, 0x01},
                     {AES_CTRL_SHADOWED_MODE_OFFSET, 0x02},
-                    {AES_CTRL_SHADOWED_OPERATION_BIT, false},
+                    {AES_CTRL_SHADOWED_OPERATION_OFFSET, 0x1},
                     {AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, false},
                     {AES_CTRL_SHADOWED_FORCE_ZERO_MASKS_BIT, false}});
   }
@@ -125,7 +126,7 @@
     EXPECT_WRITE32(AES_CTRL_SHADOWED_REG_OFFSET,
                    {{AES_CTRL_SHADOWED_KEY_LEN_OFFSET, 0x01},
                     {AES_CTRL_SHADOWED_MODE_OFFSET, 0x04},
-                    {AES_CTRL_SHADOWED_OPERATION_BIT, false},
+                    {AES_CTRL_SHADOWED_OPERATION_OFFSET, 0x1},
                     {AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, false},
                     {AES_CTRL_SHADOWED_FORCE_ZERO_MASKS_BIT, false}});
   }
@@ -145,7 +146,7 @@
     EXPECT_WRITE32(AES_CTRL_SHADOWED_REG_OFFSET,
                    {{AES_CTRL_SHADOWED_KEY_LEN_OFFSET, 0x01},
                     {AES_CTRL_SHADOWED_MODE_OFFSET, 0x08},
-                    {AES_CTRL_SHADOWED_OPERATION_BIT, false},
+                    {AES_CTRL_SHADOWED_OPERATION_OFFSET, 0x1},
                     {AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, false},
                     {AES_CTRL_SHADOWED_FORCE_ZERO_MASKS_BIT, false}});
   }
@@ -165,7 +166,7 @@
     EXPECT_WRITE32(AES_CTRL_SHADOWED_REG_OFFSET,
                    {{AES_CTRL_SHADOWED_KEY_LEN_OFFSET, 0x01},
                     {AES_CTRL_SHADOWED_MODE_OFFSET, 0x10},
-                    {AES_CTRL_SHADOWED_OPERATION_BIT, false},
+                    {AES_CTRL_SHADOWED_OPERATION_OFFSET, 0x1},
                     {AES_CTRL_SHADOWED_MANUAL_OPERATION_BIT, false},
                     {AES_CTRL_SHADOWED_FORCE_ZERO_MASKS_BIT, false}});
   }
diff --git a/sw/device/sca/aes_serial.c b/sw/device/sca/aes_serial.c
index 5dc1286..a4d57e1 100644
--- a/sw/device/sca/aes_serial.c
+++ b/sw/device/sca/aes_serial.c
@@ -54,10 +54,11 @@
 static void aes_serial_set_key(const uint8_t *key, size_t key_len) {
   SS_CHECK(key_len == kAesKeyLength);
   dif_aes_transaction_t transaction = {
+      .operation = kDifAesOperationEncrypt,
+      .mode = kDifAesModeEcb,
       .key_len = kDifAesKey128,
       .masking = kDifAesMaskingInternalPrng,
-      .mode = kDifAesModeEncrypt,
-      .operation = kDifAesOperationManual,
+      .manual_operation = kDifAesManualOperationManual,
   };
   dif_aes_key_share_t key_shares;
   memcpy(key_shares.share0, key, sizeof(key_shares.share0));
diff --git a/sw/device/tests/aes_smoketest.c b/sw/device/tests/aes_smoketest.c
index ab6596f..26c7a27 100644
--- a/sw/device/tests/aes_smoketest.c
+++ b/sw/device/tests/aes_smoketest.c
@@ -88,9 +88,10 @@
 
   // Setup ECB encryption transaction.
   dif_aes_transaction_t transaction = {
+      .operation = kDifAesOperationEncrypt,
+      .mode = kDifAesModeEcb,
       .key_len = kDifAesKey256,
-      .mode = kDifAesModeEncrypt,
-      .operation = kDifAesOperationAuto,
+      .manual_operation = kDifAesManualOperationAuto,
   };
   CHECK_DIF_OK(dif_aes_start_ecb(&aes, &transaction, key));
 
@@ -122,7 +123,7 @@
   }
 
   // Setup ECB decryption transaction.
-  transaction.mode = kDifAesModeDecrypt;
+  transaction.operation = kDifAesOperationDecrypt;
   CHECK_DIF_OK(dif_aes_start_ecb(&aes, &transaction, key));
 
   // Load the previously produced cipher text to start the decryption operation.