0
\$\begingroup\$

I am working on an AES encryptor/decryptor project that can handled AES-128, AES-192, and AES-256. The cipher key length is chosen at compile time. The module I am working on generates the key schedule. The algorithm to do so varies slightly for AES 256 vs the other two, so I have two different functions to handle them. My key expansion module has only one combinational logic block:

always_comb
begin

    expandedKey.expBlocks[0] = key;

    for (int i=1; i<=`NUM_KEY_EXP_ROUNDS; ++i)
        expandedKey.expBlocks[i] = expandBlock(i, expandedKey.expBlocks[i-1]);

end

These are the functions the previous code may call:

`ifndef AES_256 // expanding a 128- or 192-bit key

    function automatic expandBlock(input integer round, expKeyBlock_t prevBlock);    
        // ... function body
    endfunction

`else // Expanding a 256-bit key

    function automatic expandBlock(input integer round, expKeyBlock_t prevBlock);    
        // ... function body    
    endfunction

`endif // `ifndef AES_256

The problem with this configuration is that it makes testing difficult. In my testbench, I want to be able to instantiate this module in each AES_XXX configuration and simulate all three without recompiling to make regression testing easier.

I've considered using parameters instead:

`ifndef AES_256 // expanding a 128- or 192-bit key
parameter logic SELECT_AES_256 = 1'b0;
`else // Expanding a 256-bit key
parameter logic SELECT_AES_256 = 1'b1;
`endif // `ifndef AES_256

always_comb
begin

    expandedKey.expBlocks[0] = key;

    // I chose to put the SELECT_AES_256 check outside the loop so it isn't
    // checked on every loop iteration, potentially generating even more extra
    // hardware

    if (SELECT_AES_256)
        for (int i=1; i<=`NUM_KEY_EXP_ROUNDS; ++i)
            expandedKey.expBlocks[i] = expandBlock_long(i, expandedKey.expBlocks[i-1]);
    else
        for (int i=1; i<=`NUM_KEY_EXP_ROUNDS; ++i)
            expandedKey.expBlocks[i] = expandBlock_short(i, expandedKey.expBlocks[i-1]);

end

function automatic expandBlock_short(input integer expRound, expKeyBlock_t prevBlock);
        // ... function body
endfunction

function automatic expandBlock_long(input integer expRound, expKeyBlock_t prevBlock);
        // ... function body
endfunction

Using a parameter would allow me to instantiate the module in whatever configuration I want, but it has duplicated code, and I'll eventually be relying on synthesizer optimizations to eliminate unused hardware. I don't like either of those attributes.

The Question
Is there a way I can refactor this code so that I can choose which expandBlock function is called when the module is instantiated, without the duplicated code and potentially duplicated hardware?

\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

Synthesis tools are good about eliminating dead code, but you could put both the always_comb block and the function declaration in a generate-if block

if (SELECT_AES_256) begin : long_block

    always_comb
      begin
        expandedKey.expBlocks[0] = key;
        for (int i=1; i<=`NUM_KEY_EXP_ROUNDS; ++i)
                expandedKey.expBlocks[i] = expandBlock(i, expandedKey.expBlocks[i-1]);
      end

    function automatic expandBlock(input integer expRound, expKeyBlock_t prevBlock);
            // ... function body
    endfunction
end : long_block
else begin : short_block
 always_comb
      begin
        expandedKey.expBlocks[0] = key;
        for (int i=1; i<=`NUM_KEY_EXP_ROUNDS; ++i)
                expandedKey.expBlocks[i] = expandBlock(i, expandedKey.expBlocks[i-1]);
      end

    function automatic expandBlock(input integer expRound, expKeyBlock_t prevBlock);
            // ... function body
    endfunction
end : short_block
\$\endgroup\$
1
  • \$\begingroup\$ The suggestion to use a generate block is perfect, though my implementation ended up being a bit different. There was no need to put the always_comb block in the generate block. It was the same either way, as long as both generated functions have the same name. Your example code could be a bit better though, if you include the generate and endgenerate statements. \$\endgroup\$
    – skrrgwasme
    May 15, 2016 at 20:59
0
\$\begingroup\$

Something like:

`ifndef AES_256 // expanding a 128- or 192-bit key
`define theExpandBlock expandBlock_long
`else // Expanding a 256-bit key
`define theExpandBlock expandBlock_short
`endif // `ifndef AES_256

...
expandedKey.expBlocks[i] = `theExpandBlock(...
...

perhaps?

\$\endgroup\$
1
  • \$\begingroup\$ But how would I override the macro when I instantiate the module in my testbench? This doesn't seem anymore useful than my first code example. The function handle would have already been assigned at compile time when the preprocessor ran. \$\endgroup\$
    – skrrgwasme
    May 14, 2016 at 22:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.