[sw,otbnsim] Add KMAC interface to OTBNsim#29025
[sw,otbnsim] Add KMAC interface to OTBNsim#29025h-filali wants to merge 4 commits intolowRISC:masterfrom
Conversation
93a47e0 to
860684f
Compare
etterli
left a comment
There was a problem hiding this comment.
Thanks for this first implementation! It looks pretty good.
I reviewed the CSR/WSR and integration part but not yet completely the kmac.py as well as the software test. My feedback is mostly about error handling, otherwise the concept looks good to me.
ad88033 to
2e98ad2
Compare
|
Thanks @etterli for your comments. Nice finds! I added some TODOs to make sure your comments aren't forgotten. I also adapted the documentation. |
ec230c6 to
1c22c51
Compare
bed7fb1 to
b12d29d
Compare
rswarbrick
left a comment
There was a problem hiding this comment.
I've only read the "Generalize WSRs" commit so far, so here are just some partial comments. But I really like this! I like the way that csr.py and wsr.py can be merged so much.
rswarbrick
left a comment
There was a problem hiding this comment.
I've only read part of the "Add new KMAC model" commit. I think it could be dramatically simpler, and I'm sorry for not having reviewed it a few weeks ago.
1459d48 to
40376cb
Compare
|
Thanks @rswarbrick for your review. I changed the following things to address your feedback:
Do you think you could take another look @rswarbrick ? |
40376cb to
57a97ad
Compare
hw/ip/otbn/dv/otbnsim/sim/wsr.py
Outdated
| from typing import List, Optional, Tuple | ||
| from .ext_regs import OTBNExtRegs | ||
| from .ispr import ISPR, DumbISPR, ISPRChange | ||
| from .kmac_ispr import KmacDataWSR |
There was a problem hiding this comment.
This kmac_ispr import is not supposed to be in this commit. It should come in the next one.
rswarbrick
left a comment
There was a problem hiding this comment.
Lots and lots of comments, sorry. I guess this is the danger of an enormous PR :-/
hw/ip/otbn/dv/otbnsim/sim/wsr.py
Outdated
| from typing import List, Optional, Tuple | ||
| from .ext_regs import OTBNExtRegs | ||
| from .ispr import ISPR, DumbISPR, ISPRChange | ||
| from .kmac_ispr import KmacDataWSR |
There was a problem hiding this comment.
I don't think that file exists in this commit :-)
There was a problem hiding this comment.
This still needs fixing, I think. And Pascal pointed it out too.
hw/ip/otbn/dv/otbnsim/sim/wsr.py
Outdated
| Assumes that idx is a valid index (call check_idx to ensure this). | ||
|
|
||
| ''' | ||
| # KMAC_DATA_S0/1 should only be through the wrapper class. |
| if idx == 0x8: | ||
| return self.KMAC_DATA.read_unsigned(share_idx=0) | ||
|
|
||
| elif idx == 0x9: | ||
| return self.KMAC_DATA.read_unsigned(share_idx=1) |
There was a problem hiding this comment.
If we here extract the read for the KMAC data WSRs (which makes sense) then I think we should not have self.KMAC_DATA.shares[0] and self.KMAC_DATA.shares[1] in _by_idx. Otherwise the comment above saying that these should only be accessed via the wrapper is a little bit contradictory.
There was a problem hiding this comment.
Ah, now I see why it is in _by_idx. It is there to simplify the on_start() and also we need these WSRs indexes to be present for check_idx() and has_value_at_idx(). These two are used in the BNWSRR and BNWSRW instructions to check whether the access is legal.
There was a problem hiding this comment.
As this read_at_idx() is the only place where _by_idx is accessed to read the WSRs I agree that these should stay in _by_idx (same reasoning for write_at_idx()).
| # Set the message write error if an illegal write to KMAC_DATA_S0/1 happened. | ||
| if self._wsrs.KMAC_DATA.shares_dirty() and \ | ||
| not self._csrs.KMAC_IF_STATUS.get_msg_write_rdy(): | ||
| self._csrs.KMAC_IF_STATUS.set_msg_write_error() |
There was a problem hiding this comment.
| # Set the message write error if an illegal write to KMAC_DATA_S0/1 happened. | |
| if self._wsrs.KMAC_DATA.shares_dirty() and \ | |
| not self._csrs.KMAC_IF_STATUS.get_msg_write_rdy(): | |
| self._csrs.KMAC_IF_STATUS.set_msg_write_error() | |
| # Set the message write error if an illegal write to KMAC_DATA_S0/1 happened. | |
| if (self._wsrs.KMAC_DATA.shares_dirty() and | |
| not self._csrs.KMAC_IF_STATUS.get_msg_write_rdy()): | |
| self._csrs.KMAC_IF_STATUS.set_msg_write_error() |
hw/ip/otbn/dv/otbnsim/sim/kmac.py
Outdated
| entry = None | ||
| if mode is not None and strength is not None: | ||
| entry = MODE_STRENGTH_TABLE.get((mode, strength)) |
There was a problem hiding this comment.
The .get() will return None by default if there is no match. You could specify another default if desired with .get(<key>, <YourDefault>).
| entry = None | |
| if mode is not None and strength is not None: | |
| entry = MODE_STRENGTH_TABLE.get((mode, strength)) | |
| entry = MODE_STRENGTH_TABLE.get((mode, strength)) |
c0c2c71 to
cefe00b
Compare
|
Thanks @rswarbrick @etterli please LMK if this LGTY now! |
cefe00b to
b8d2f8a
Compare
| self._keccak_round_ctr.decrement() | ||
|
|
||
| # Handle KMAC_MSG_SEND command. | ||
| kmac_msg_send = self._csrs.KMAC_MSG_SEND.read_unsigned() |
There was a problem hiding this comment.
Thanks for the tweak (after this comment). I think you can get rid of the variable too: if self._csrs.KMAC_MSG_SEND.read_unsigned(): ?
There was a problem hiding this comment.
The variable is reused later in the code, where it is useful for not going over the 100 char limit.
hw/ip/otbn/dv/otbnsim/sim/kmac.py
Outdated
| This prevents updates within a cycle from taking effect until end_cycle() is called. | ||
| """ | ||
|
|
||
| def __init__(self, max_val: Optional[int] = None, min_val: int = 0) -> None: |
There was a problem hiding this comment.
Do you ever supply min_val? If not, I'd probably remove the argument (for now, at least).
| class Counter(): | ||
| """A hardware-like counter model with separate Next (D) and Current (Q) states. | ||
|
|
||
| This prevents updates within a cycle from taking effect until end_cycle() is called. |
There was a problem hiding this comment.
It might be worth adding a note to the documentation that the value of the counter will always stay in the bounds?
hw/ip/otbn/dv/otbnsim/sim/kmac.py
Outdated
| # Determine how many bytes are valid for this word from BYTE_STROBE. | ||
| byte_strobe = self._csrs.KMAC_BYTE_STROBE.read_unsigned() | ||
| byte_mask = (byte_strobe >> strobe_shift) & strobe_mask | ||
| num_bytes = byte_mask.bit_count() |
There was a problem hiding this comment.
The change looks good to me. Could you open a "what should happen for non-contiguous masks" issue? The behaviour needs documenting (even if we change our mind in the future).
| # In case of a write, commit the next state. | ||
| if self._next_value is not None: | ||
| self._value = self._next_value | ||
| # In case we are writing a command it needs to be cleared in the next cycle. |
There was a problem hiding this comment.
I'm really sorry: I know that I basically said the same thing three lines earlier: I must have missed this comment. It needs a comma after commend. There's also no really need for "in case": just this, maybe?
# If we are writing a command, it needs to be cleared in the next cycle.
(Aargh! I even copy-pasted the text in my proposed reordering. Sorry)
There was a problem hiding this comment.
No worries, I'll fix it!
| - name: kmac_if_status | ||
| address: 0x7d9 | ||
| doc: | | ||
| Write a 1 to bits 1 or 2 to clear the error bits. |
There was a problem hiding this comment.
The fields of this register need to explicitly say what happens on a write. (I'd hope to see "w1c" or "write-one-clear" in the text somewhere, I think)
hw/ip/otbn/data/csr.yml
Outdated
| - name: kmac_intr | ||
| address: 0x7da | ||
| doc: | | ||
| Writing 1 to bit 0 of this register clears the interrupt. |
There was a problem hiding this comment.
Probably better to move this information into the field and just leave the second line at the register level.
hw/ip/otbn/data/csr.yml
Outdated
| - name: kmac_cfg | ||
| address: 0x7db | ||
| doc: | | ||
| This register allows OTBN to set the KMAC hashing mode (mode = SHAKE / cSHAKE / SHA3), enable or disable keyed KMAC mode (kmac_en = 0/1), and select the desired security strength (kstrength = L128 / L224 / L256 / L384 / L512). |
There was a problem hiding this comment.
This is essentially a long sentence with a list of the fields :-) I think the sets of allowed values belong on the fields below. This line could probably be much shorter. It could probably be really short, I think? "A configuration register for the KMAC interface."
hw/ip/otbn/data/csr.yml
Outdated
| - name: kmac_msg_send | ||
| address: 0x7dc | ||
| doc: | | ||
| This CSR consists of a single bit that, if set to one, will trigger OTBN to send the contents of KMAC_DATA_S0 and KMAC_DATA_S1 to KMAC. |
There was a problem hiding this comment.
I think this can be shorter: you've defined the field below anyway, haven't you?
hw/ip/otbn/data/csr.yml
Outdated
| - name: kmac_cmd | ||
| address: 0x7dd | ||
| doc: | | ||
| The register allows OTBN to issue START, PROCESS, RUN and DONE commands to KMAC. |
There was a problem hiding this comment.
Probably better to move the list of commands to the CMD field below?
hw/ip/otbn/data/csr.yml
Outdated
| doc: | | ||
| Writes to this register are ignored. | ||
| This register exposes the error code from the KMAC HWIP ERR_CODE register. | ||
| The additional debug information from the KMAC HWIP ERR_CODE register is not displayed. |
There was a problem hiding this comment.
Nit: I'd probably use the same verb as the previous line (because I'm not sure what "display" means). I think we can also avoid a double negative with something like "No other information from the KMAC HWIP ERR_CODE register is exposed."
hw/ip/otbn/data/wsr.yml
Outdated
|
|
||
| If masking is not required: | ||
| - Set one share (e.g., KMAC_DATA_S0) to the plaintext data. | ||
| - Set the corresponding share (e.g., KMAC_DATA_S1) to all-zeros. |
There was a problem hiding this comment.
I think you probably want "other share" here (and the same for KMAC_DATA_S1 below)
hw/ip/otbn/dv/otbnsim/sim/csr.py
Outdated
| # RND_PREFETCH register | ||
| return 0 | ||
|
|
||
| if idx in {0x7d9, 0x7da, 0x7db, 0x7dc, 0x7dd, 0x7de, 0xfc2, 0xfc3}: |
There was a problem hiding this comment.
It might be cleaner to do it like this, avoiding repeating the indices and lookup:
kmac_reg = self._by_idx.get(idx)
if kmac_reg is not None:
return kmac_reg.read_unsigned()
The same goes for the writes below.
hw/ip/otbn/dv/otbnsim/sim/csr.py
Outdated
| 0xfc3, # KMAC_ERROR | ||
| } | ||
|
|
||
| self._by_idx = { |
There was a problem hiding this comment.
I think this name could be more informative. Maybe something like _idx_to_kmac_reg or something?
| self.KMAC_CMD = KmacCommandCSR('KMAC_CMD', write_mask=0x3f) | ||
| self.KMAC_BYTE_STROBE = DumbISPR('KMAC_BYTE_STROBE', width=32) | ||
|
|
||
| self._known_indices = { |
There was a problem hiding this comment.
It's a pity that we have to repeat the addresses below when we're doing the lookups. Couldn't this be something like the following?
self._idx_to_csr = {
0x7c0: self.flags.groups[0] # FG0
0x7c1: self.flags.groups[1] # FG1
0x7c8: self.flags # The typing is wrong here! But I think we just
# need FlagGroups to derive from ISPR
# 0x7d0 - 0x7d8 really aren't standard CSRs. Probably they need
# special-casing in check_idx / read_unsigned / write_unsigned
0x7d8: ??? # I'd suggest modelling RND_PREFETCH behaviour
# explicitly (since the modelling is currently split
# between read_unsigned and write_unsigned
0x7d9: self.KMAC_IF_STATUS,
0x7da: self.KMAC_INTR,
0x7db: self.KMAC_CFG,
0x7dc: self.KMAC_MSG_SEND,
0x7dd: self.KMAC_CMD,
0x7de: self.KMAC_BYTE_STROBE,
0xfc0: ???
0xfc1: ??? # Similarly, I'd suggest modelling the CSR interface to
# RND and URND explicitly. To avoid the circular
# dependency, I'd suggest defining CSRs in terms of WSRs
# and making WSRFile an argument to the CSRFile
# constructor.
0xfc2: self.KMAC_STATUS,
0xfc3: self.KMAC_ERROR,
}There was a problem hiding this comment.
I tried to find a generic solution that works for all the "special" registers. Please check if that fits your vision.
b8d2f8a to
387b661
Compare
|
Thanks @rswarbrick for having another look. Your feedback should be addressed now. PTAL |
387b661 to
cffde69
Compare
This commit changes the WSR and DumbWSR classes to be more generic. This allows code sharing between the WSR classes and potential new classes with different register size but the same functionality. Signed-off-by: Hakim Filali <[email protected]>
This commit adds a new KMAC model that can be used to model a KMAC-OBTN interface. This model can be used to interface KMAC from OTBN. This model should behave like the KMAC HW IP. Signed-off-by: Hakim Filali <[email protected]>
cffde69 to
33cdedd
Compare
Use the new KMAC OTBNsim model to extend OTBNsim. This commit adds new WSRs and CSRs to OTBNsim and connects them to the new KMAC model. Signed-off-by: Hakim Filali <[email protected]>
This commit adds an assembly test that tests all allowed combinations of KMAC modes and kstrengths. This commit shall also serve as a first example of how to use the interface. Signed-off-by: Hakim Filali <[email protected]>
33cdedd to
ea3d56c
Compare
This PR adds a new KMAC interface to OTBNsim.
This PR is rebased on #29165 so please review the first commit there.
The second commit unifies the classes for the CSRs and WSRs.
The third commit adds an implementation for the model.
The fourth commit connects the KMAC model to OTBN.
The fifth commit contains a working assembly example/test.