Skip to content

Zvkned: add infrastructure for Zvkned #752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

KotorinMinami
Copy link
Contributor

Implements the Zvkned (NIST Suite: Vector AES Block Cipher) extension, as of version Release 1.0.0.

The following instructions are included:

  • vaesef.[vv,vs]
  • vaesem.[vv,vs]
  • vaesdf.[vv,vs]
  • vaesdm.[vv,vs]
  • vaeskf1.vi
  • vaeskf2.vi
  • vaesz.vs

All instructions were tested with VLEN=128 and results were compared with QEMU results of each instruction.

Note
This PR was originally developed by @charmitro , and now the Vector AES Block Cipher has been released.

Copy link

github-actions bot commented Feb 20, 2025

Test Results

400 tests  ±0   400 ✅ ±0   1m 48s ⏱️ ±0s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 4f8b826. ± Comparison against base commit 32d5194.

♻️ This comment has been updated with latest results.

Copy link
Collaborator

@Timmmm Timmmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few random low-level Sail comments (I know nothing about the actual spec).

Also how was it tested against QEMU exactly? It would be nice to integrate tests like that (in future; we don't have the infrastructure for it yet).

let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let num_elem = get_num_elem(LMUL_pow, SEW);

if (zvk_check_elements(VLEN, num_elem, LMUL, SEW) == false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (zvk_check_elements(VLEN, num_elem, LMUL, SEW) == false)
if not(zvk_check_elements(VLEN, num_elem, LMUL, SEW))

(And in the other places.)


val aes_rotword : bits(32) -> bits(32)
function aes_rotword(x) = {
let a0 : bits (8) = x[ 7.. 0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would just simplify this to

function aes_rotword(x) = x[7 .. 0] @ x[15 .. 8] @ x[23 .. 16] @ x[31 .. 24]

} else {
let 'n = num_elem;
let 'm = SEW;
assert('m == 32);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessarily true? Definitely worth a comment explaining why at least.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the spec declared that it should be 32 bits, and without that, the Sail compiler will chuck an error, because it won't know how many bits 'm has.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm the spec says it is reserved. I think we should maybe raise an illegal instruction exception in that case (though we still need a proper policy on how to handle reserved behaviour).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at the spec closely enough yet, but if it is reserved it seems like it would be best to catch it at the encdec stage if possible and just not decode it as this instruction. Then it would fall through to the existing illegal instruction catch-all.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that probably makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but then bits('m) would need to be bits(32) for Sail to prove it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do something like this:

  let 'SEW      = get_sew();
  let LMUL_pow = get_lmul_pow();
  let LMUL     = if LMUL_pow < 0 then 0 else LMUL_pow;
  let 'num_elem = get_num_elem(LMUL_pow, SEW);

  if 'SEW != 32 | not(zvk_check_elements(VLEN, num_elem, LMUL, SEW)) then {
    handle_illegal();
    RETIRE_FAIL
  } else {
    let vs2_val = read_vreg(num_elem, SEW, LMUL_pow, vs2);
    let vd_val  = read_vreg(num_elem, SEW, LMUL_pow, vd);

    let eg_len = (unsigned(vl) / 'num_elem);
   ...

I think that will compile. Not 100% sure though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works, thank you!


let eg_len = (unsigned(vl) / 'n);
let eg_start = (unsigned(vstart) / 'n);
let keyelem = if suffix == "vv" then 1 else 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should avoid having behaviour encoded in strings. suffix should be an enum, not strings.

assert(0 <= ((i * 4) + 3) & ((i * 4) + 3) < 'n);

let vd_state = get_velem(vd_val, i);
let vs2_key = get_velem(vs2_val, keyelem * i);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this would be something like:

let vs2_key = get_velem(vs2_val, match suffix { something_VV => i, something_VS => 0 });

(I'll leave you to come up with suitable names for whatever VV and VS mean!)

let vs2_key = get_velem(vs2_val, keyelem * i);
let sb : bitvector(128) = aes_subbytes_fwd(vd_state);
let sr : bitvector(128) = aes_shift_rows_fwd(sb);
let ark : bitvector(128) = sr ^ vs2_key;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bits? Maybe before my time; is bitvector an old version of bits?

let sb : bitvector(128, dec) = aes_subbytes_fwd(vd_state);
let sr : bitvector(128, dec) = aes_shift_rows_fwd(sb);
let mix : bitvector(128, dec) = aes_mixcolumns_fwd(sr);
let ark : bitvector(128, dec) = mix ^ vs2_key;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bits(128). You could just delete all these, though it may be appropriate for readability.

assert('m == 32);

let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2);
let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove , dec - Sail doesn't support per-vector orders now.

You could just delete the type annotations (I would, but up to you).

Comment on lines 201 to 158
/* VAESDM.VV */

mapping vaesdm_mnemonic : bits(7) <-> string = {
0b1010001 <-> "vaesem.vv",
0b1010011 <-> "vaesem.vs",
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment and mapping name states vaesdm but the mapping itself is happening for vaesem.[vv, vs] mnemonics.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the rest of the model maps ast to assembly; not the bits directly.

Copy link
Collaborator

@jordancarlin jordancarlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to take a look at the spec, but here are some initial comments.

function get_velem(v, i) = v[4 * i + 3] @ v[4 * i + 2] @ v[4 * i + 1] @ v[4 * i]

val zvk_check_elements : (int, int, int, int) -> bool
function zvk_check_elements(VLEN, num_elem, LMUL, SEW) = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With simple function declarations like this, it’s usually easier to eliminate the val line and directly integrate the types into the function line.

@@ -23,6 +23,7 @@ enum clause extension = Ext_C
enum clause extension = Ext_B
// Vector Operations
enum clause extension = Ext_V
enum clause extension = Ext_Zvkned
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing comment with long form name of the extension. This should also be moved down to be with the rest of the Z* extensions in a new Zv* section.

Comment on lines 359 to 361
let current_round_key = get_velem(vs2_val, i);

let round_key_b = get_velem(vs2_val, i);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specification states:

  let CurrentRoundKey[3:0] : bits(128) = get_velem(vs2, EGW=128, i);
  let RoundKeyB[3:0] : bits(32) = get_velem(vd, EGW=128, i); // Previous round key

@KotorinMinami KotorinMinami force-pushed the zvkned branch 3 times, most recently from 9826514 to 3161d3c Compare February 24, 2025 14:56
@jordancarlin jordancarlin added the extension Adds support for a RISC-V extension label Mar 5, 2025
@KotorinMinami
Copy link
Contributor Author

I think the issues raised by the review have been resolved so far. @Timmmm @jordancarlin Is there anything else wrong with my code?

@jordancarlin
Copy link
Collaborator

@KotorinMinami Sorry about the delay in reviewing this. I think it needs some modifications because of the recent type refactoring for registers. Once it is rebased I'll give it another look. Would be good to get this one in soon for RVA23.

@KotorinMinami
Copy link
Contributor Author

I think we can move forward with this. @jordancarlin

let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let 'num_elem = get_num_elem(LMUL_pow, SEW);

if 'SEW != 32 | (not(zvk_check_elements(VLEN, num_elem, LMUL, SEW)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you still need that? Its part of the encdec clause already (get_sew() == 32)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about even moving the zvk_check_elements check into the encdec clause?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need. The Sail compiler cannot obtain specific SEW information here (even if proven in encdec). Without specifying SEW, it will fail type checking.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's just to make the Sail compiler happy then how about using an assert?


/* VAESKF2.VI */

union clause ast = RISCV_VAESKF2_VI : (vregidx, bits(5), vregidx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the RISCV_* from all instruction names.

Suggested change
union clause ast = RISCV_VAESKF2_VI : (vregidx, bits(5), vregidx)
union clause ast = VAESKF2_VI : (vregidx, bits(5), vregidx)


mapping clause encdec = RISCV_VAESKF2_VI(vs2, rnd, vd)
<-> 0b1010101 @ encdec_vreg(vs2) @ rnd @ 0b010 @ encdec_vreg(vd) @ 0b1110111
when extensionEnabled(Ext_Zvkned) & get_sew() == 32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
when extensionEnabled(Ext_Zvkned) & get_sew() == 32
when extensionEnabled(Ext_Zvkned) & get_sew() == 32

Spaces

Comment on lines 299 to 302
let 'SEW = get_sew();
let LMUL_pow = get_lmul_pow();
let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let 'num_elem = get_num_elem(LMUL_pow, 'SEW);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting

Suggested change
let 'SEW = get_sew();
let LMUL_pow = get_lmul_pow();
let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let 'num_elem = get_num_elem(LMUL_pow, 'SEW);
let 'SEW = get_sew();
let LMUL_pow = get_lmul_pow();
let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let 'num_elem = get_num_elem(LMUL_pow, SEW);

Comment on lines 304 to 320
if 'SEW != 32 | (not(zvk_check_elements(VLEN, num_elem, LMUL, 'SEW)))
then {
handle_illegal();
RETIRE_FAIL
} else {
var rnd_val : bits(4) = rnd[3..0];
let vs2_val = read_vreg(num_elem, SEW, LMUL_pow, vs2);
let vd_val = read_vreg(num_elem, SEW, LMUL_pow, vd);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you use 'SEW and sometimes SEW? Probably a typo?

@@ -100,6 +100,9 @@ enum clause extension = Ext_Zksed
// Scalar & Entropy Source Instructions: ShangMi Suite: SM3 Hash Cipher Instructions
enum clause extension = Ext_Zksh

// Vector Instructions: NIST Suite: Vector AES Block Cipher
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s put this with the existing Zvk extensions.

let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let 'num_elem = get_num_elem(LMUL_pow, SEW);

if 'SEW != 32 | (not(zvk_check_elements(VLEN, num_elem, LMUL, SEW)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about even moving the zvk_check_elements check into the encdec clause?

let sr = aes_shift_rows_fwd(sb);
let ark = sr ^ vs2_key;

write_single_element(128, i, vd, ark);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing an element of size 128 is not possible. This will cause a runtime assertion failure.

write_single_element() invokes read_single_vreg(), which contains the assertion:
assert(8 <= SEW && SEW <= 64);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, why does write_single_element even allow 128-bit EEW? If it's going to trigger an assertion, the function definition shouldn't permit a 128-bit EEW (Element Effective Width) in the first place. If that's the case, then we'll just have to perform two 64-bit reads.

val write_single_element : forall 'm 'x, 8 <= 'm <= 128. (int('m), int('x), vregidx, bits('m)) -> unit

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. It looks like some of the type signatures on these helpers are insufficiently precise.

@KotorinMinami KotorinMinami force-pushed the zvkned branch 2 times, most recently from 6e7e764 to 86fbdeb Compare April 2, 2025 08:00
Comment on lines 31 to 39
function zvk_check_elements(VLEN : int, num_elem : int, LMUL : int, SEW : int) -> bool = {
((unsigned(vl)%num_elem) != 0) | ((unsigned(vstart)%num_elem) != 0) | (LMUL*VLEN) < (num_elem*SEW)
}

function zvk_check_encdec() -> bool = {
let SEW = get_sew();
let LMUL_pow = get_lmul_pow();
let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let num_elem = get_num_elem(LMUL_pow, SEW);

zvk_check_elements(VLEN, num_elem, LMUL, SEW)
}
Copy link
Contributor

@nadime15 nadime15 Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the idea of zvk_check_encdec! We can use this for the other Zvk* extensions too!

Personally, I prefer something like:

Suggested change
function zvk_check_elements(VLEN : int, num_elem : int, LMUL : int, SEW : int) -> bool = {
((unsigned(vl)%num_elem) != 0) | ((unsigned(vstart)%num_elem) != 0) | (LMUL*VLEN) < (num_elem*SEW)
}
function zvk_check_encdec() -> bool = {
let SEW = get_sew();
let LMUL_pow = get_lmul_pow();
let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;
let num_elem = get_num_elem(LMUL_pow, SEW);
zvk_check_elements(VLEN, num_elem, LMUL, SEW)
}
function zvk_check_encdec() -> bool = {
let LMUL_pow = get_lmul_pow();
(unsigned(vl) % 4 == 0) & (unsigned(vstart) % 4 == 0) | (2 ^ LMUL_pow * VLEN) >= 128)
}

The spec sets EGW=128 and EGS=4

The spec also says that vl and vstart must be multiples of 4 (=EGS), not num_elem. This means we could have vl = 8 and num_elem = 8, which is valid, but the current check (vl % num_elem != 0) would incorrectly be false when it should be true.

Additionally, the code currently takes the exponent of the current LMUL (get_lmul_pow()) but ignores negative exponents (!). Right now, if you check get_lmul_pow(), the condition (LMUL*VLEN) < (num_elem*SEW) effectively does:

LMUL_pow * VLEN < (2 ^ LMUL_pow * VLEN / SEW) * SEW

which simplifies to LMUL_pow < (2 ^ LMUL_pow) Which is always true (for now).

What we really want is

2 * get_lmul_pow() * VLEN >= 128 (I assume that sail can deal with negative exponents)

Edit: It would probable make even more sense to have something like

function zvk_check_encdec(EGW : int, EGS: int)

because EGW for vsha2c[hl].vv varies and is EGW = 4 * SEW

Comment on lines 84 to 85
let eg_len = (unsigned(vl) / num_elem);
let eg_start = (unsigned(vstart) / num_elem);
Copy link
Contributor

@nadime15 nadime15 Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here for EGS = 4:

Suggested change
let eg_len = (unsigned(vl) / num_elem);
let eg_start = (unsigned(vstart) / num_elem);
let eg_len = (unsigned(vl) / 4);
let eg_start = (unsigned(vstart) / 4;

I ran into cases where vl = 4 and vstart = 0, but num_elem = 8 (which is valid). However, with the old code, we would incorrectly skip the following loop because eg_len would be 0, even though it should be 1.

Comment on lines 18 to 27
val set_velem : forall 'n 'm 'p, 'n > 0 & 'm > 0 & 'p >= 0 & 4 * 'p + 3 < 'n . (vector('n, bits('m)), bits(4 * 'm), int('p)) -> vector('n, bits('m))
function set_velem(s, v, i) = {
let m = length(v) / 4;
[s with
(4 * i) = v[m - 1 .. 0],
(4 * i + 1) = v[2 * m - 1 .. m],
(4 * i + 2) = v[3 * m - 1 .. 2 * m],
(4 * i + 3) = v[4 * m - 1 .. 3 * m],
]
}
Copy link
Contributor

@nadime15 nadime15 Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For #819, I added my own version of the set_velem function, but I actually prefer yours! One suggestion: instead of returning s, how about we write directly to vd within the function and then call write_vreg? Since it's a setter method, it might make more sense to do it that way and we would follow the spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, there's a problem: this approach results in multiple calls to write_vreg, subsequently leading to redundant calls to write_single_vreg, making it less efficient.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you are right its not really efficient but Sail's primary focus is readability.

Currently, Sail does not provide a function to write n continuous elements starting from a specific position in a given vreg. The available functions for writing to a vreg are:

  • write_vreg()
  • write_single_vreg()
  • write_single_element()

The implementation I have is:

/* Divide the input bitvector into 4 equal slices and store them in vd starting at position 4*i */
val set_velem : forall 'p 'n 'm, 8 <= 'n <= 64 & 'm > 0 & 'p >= 0. (vregidx, int('n), bits('m), int('p)) -> unit
function set_velem(vd, SEW, input, i) = {
  foreach(j from 0 to 3)
    write_single_element(SEW, 4 * i + j, vd, slice(input, j * SEW, SEW));
}

This function enables direct writing (setting) of multiple continuous elements to a vreg without the need to return the value vector and to store it at some pointer later via write_vreg() . Additionally, it could be further generalized to allow writing n continuous elements instead of just four and it follows more the spec.

It might be helpful to get feedback from others as well. @pmundkur @jordancarlin

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add function like write_element_group ?

Comment on lines 35 to 39
function zvk_check_encdec(EGW : int, EGS : int) -> bool = {
let LMUL_pow = get_lmul_pow();
let LMUL = if LMUL_pow < 0 then 0 else LMUL_pow;

(unsigned(vl) % EGS == 0) & (unsigned(vstart) % EGS == 0) & (2 ^ LMUL * VLEN >= 128)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We more or less agreed on the same function but you use

2 ^ LMUL * VLEN >= 128

instead of

2 ^ LMUL_pow * VLEN >= 128

This essentially suggests that LMUL = 0.5 (mf2), 0.25 (mf4), 0.125 (mf8) cannot be used with this extension, which is incorrect (I still assume that Sail can handle negative exponents).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@nadime15 nadime15 Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I noticed that too while testing it with the OCaml backend and it did not work, I was surprised that it worked with the C backend. Thats why I initially assumed its supported somehow.

I propose that we go ahead and merge the function as it is for now. We can then either update it after this is merged and the remaining vector crypto extensions are in, or modify it immediately.

Personally, I think it makes more sense to keep the current version and follow up with a separate PR that explicitly changes the function.

Edit: Actually it does not matter if we change the function now or do it after all the remaining vector crypto extension are in, I am happy with both

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with both too, though we should file an issue with the details to track this. Is this a hole in the vector test suite that doesn't trigger this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because the vector test suite only generates test cases for valid configurations.

@nadime15
Copy link
Contributor

If you started reviewing the code, it’s ready for another look. @KotorinMinami and I worked together, and the code now structurally matches the other Vector Cryptography Extensions.

To support the implementation of Zvkned extension in SAIL, this
creates the necessary infrastructure(i.e., a file to hold it, and the
extension macro), preparing the tree for the Zvkned implementation.

Signed-off-by: Charalampos Mitrodimas <[email protected]>

Co-authored-by: KotorinMinami <[email protected]>
Copy link
Collaborator

@pmundkur pmundkur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty clean! Some minor fixes suggested.

@@ -21,7 +21,7 @@ function zvk_check_encdec(EGW: int, EGS: int) -> bool = (unsigned(vl) % EGS == 0
* ----------------------------------------------------------------------
*/

enum zvkfunct6 = {ZVK_VSHA2CH, ZVK_VSHA2CL}
enum zvkfunct6 = {ZVK_VAESDFVV, ZVK_VAESDFVS, ZVK_VAESDMVV, ZVK_VAESDMVS, ZVK_VAESEFVV, ZVK_VAESEFVS, ZVK_VAESEMVV, ZVK_VAESEMVS, ZVK_VSHA2CH, ZVK_VSHA2CL}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
enum zvkfunct6 = {ZVK_VAESDFVV, ZVK_VAESDFVS, ZVK_VAESDMVV, ZVK_VAESDMVS, ZVK_VAESEFVV, ZVK_VAESEFVS, ZVK_VAESEMVV, ZVK_VAESEMVS, ZVK_VSHA2CH, ZVK_VSHA2CL}
enum zvkfunct6 = {ZVK_VAESDF_VV, ZVK_VAESDF_VS, ZVK_VAESDM_VV, ZVK_VAESDM_VS, ZVK_VAESEF_VV, ZVK_VAESEF_VS, ZVK_VAESEM_VV, ZVK_VAESEM_VS, ZVK_VSHA2CH, ZVK_VSHA2CL}

This will make it a bit easier to read and is closer to the mnemonic where . is replaced by _. Their uses will need to be updated as well.


function clause currentlyEnabled(Ext_Zvkned) = hartSupports(Ext_Zvkned) & currentlyEnabled(Ext_V)

union clause ast = ZVKVAESDFTYPE : (zvkfunct6, vregidx, vregidx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
union clause ast = ZVKVAESDFTYPE : (zvkfunct6, vregidx, vregidx)
union clause ast = VAESDF : (zvkfunct6, vregidx, vregidx)

Elsewhere in the PR there are ast nodes like VAESKF1_VI, VAESZ_VS and similar, which are easier to parse; this will make it more consistent.

RETIRE_SUCCESS
}

mapping vaesdf_mnemonic : zvkfunct6 <-> string = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a partial mapping, since there are more elements in zvkfunct6 than just these two. mappings should be complete, otherwise use scattered mapping clauses.

In this case, since there are only two valid funct6 values for VAESDF and similar nodes , it might be best to split the big zvkfunct6 enumeration into {_VV, _VS} pairs for each ast node. This will also help make the above if .. else safer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, do you mean something like

enum zvk_vaesdf_funct6 = {ZVK_VAESDF_VV, ZVK_VAESDF_VS}
enum zvk_vaesdm_funct6 = {ZVK_VAESDM_VV, ZVK_VAESDM_VS}
enum zvk_vaesef_funct6 = {ZVK_VAESEF_VV, ZVK_VAESEF_VS}
enum zvk_vaesem_funct6 = {ZVK_VAESEM_VV, ZVK_VAESEM_VS}

?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly.

mapping clause assembly = ZVKVAESDFTYPE(funct6, vs2, vd)
<-> vaesdf_mnemonic(funct6) ^ sep() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2)

union clause ast = ZVKVAESDMTYPE : (zvkfunct6, vregidx, vregidx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
union clause ast = ZVKVAESDMTYPE : (zvkfunct6, vregidx, vregidx)
union clause ast = VAESDM : (zvkfunct6, vregidx, vregidx)

RETIRE_SUCCESS
}

mapping vaesdm_mnemonic : zvkfunct6 <-> string = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above; make this into one complete mapping for zvkfunct6.

mapping clause assembly = ZVKVAESDMTYPE(funct6, vs2, vd)
<-> vaesdm_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2)

union clause ast = ZVKVAESEFTYPE : (zvkfunct6, vregidx, vregidx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
union clause ast = ZVKVAESEFTYPE : (zvkfunct6, vregidx, vregidx)
union clause ast = VAESEF : (zvkfunct6, vregidx, vregidx)

assert(i * 4 + 3 < num_elem);

let state = get_velem_quad(vd_val, i);
let rkey = if funct6 == ZVK_VAESDFVV
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if ... else is safer if there are only possible values for funct6. See below.

mapping clause assembly = ZVKVAESEFTYPE(funct6, vs2, vd)
<-> vaesef_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ spc() ^ vreg_name(vs2)

union clause ast = ZVKVAESEMTYPE : (zvkfunct6, vregidx, vregidx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
union clause ast = ZVKVAESEMTYPE : (zvkfunct6, vregidx, vregidx)
union clause ast = VAESEM : (zvkfunct6, vregidx, vregidx)

RETIRE_SUCCESS
}

mapping vaesef_mnemonic : zvkfunct6 <-> string = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Splitting up zvkfunct6 will help make this mapping complete; right now, it isn't.

@KotorinMinami
Copy link
Contributor Author

I've already made the corresponding changes. Since the if section already fully covers the enum, I wanted to use match to handle it. However, I'm not sure why, without specifying the type, sail can't infer the return type of the match expression. If you think using if is still more clear, I can switch it back.

@pmundkur
Copy link
Collaborator

I've already made the corresponding changes. Since the if section already fully covers the enum, I wanted to use match to handle it. However, I'm not sure why, without specifying the type, sail can't infer the return type of the match expression. If you think using if is still more clear, I can switch it back.

The match is clearer, no needed to change.

@@ -112,6 +112,7 @@ For booting operating system images, see the information under the
- Zvbc extension for vector carryless multiplication, v1.0
- Zvkb extension for vector cryptography bit-manipulation, v1.0
- Zvknha and Zvknhb extensions for vector cryptography NIST Suite: Vector SHA-2 Secure Hash, v1.0
- Zvkned extension for vector cryptography NIST Suite: Vector AES Block Cipher, v1.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you put Zvkned after Zvknha ? In the spec Zvknh[ab] comes after Zvkned

Comment on lines 180 to 182
"Zvkned": {
"supported": true
},
Copy link
Contributor

@nadime15 nadime15 Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, why is Zvkned in between Zvknha and Zvknhb?

enum zvk_vaesef_funct6 = {ZVK_VAESEF_VV, ZVK_VAESEF_VS}
enum zvk_vaesem_funct6 = {ZVK_VAESEM_VV, ZVK_VAESEM_VS}

function aes_rotword(x : bits(32)) -> bits(32) = x[7.. 0] @ x[31..24] @ x[23..16] @ x[15.. 8]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion this function should be part of riscv_types_kext.sail

function aes_rotword(x : bits(32)) -> bits(32) = x[7.. 0] @ x[31..24] @ x[23..16] @ x[15.. 8]

Co-authored-by: KotorinMinami <[email protected]>
Copy link
Contributor

@nadime15 nadime15 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Copy link
Collaborator

@pmundkur pmundkur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extension Adds support for a RISC-V extension
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants