diff --git a/target/communication/fi_crypto_commands.py b/target/communication/fi_crypto_commands.py index 1c9df92f..eb86983d 100644 --- a/target/communication/fi_crypto_commands.py +++ b/target/communication/fi_crypto_commands.py @@ -5,178 +5,48 @@ Communication with OpenTitan happens over the uJSON command interface. """ +import copy import json import time from typing import Optional -class OTFICrypto: - def __init__(self, target) -> None: - self.target = target - - def _ujson_crypto_cmd(self) -> None: - time.sleep(0.01) - self.target.write(json.dumps("CryptoFi").encode("ascii")) - time.sleep(0.01) - - def init(self) -> None: - """ Initialize the Crypto FI code on the chip. - Returns: - The device ID of the device. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Init command. - time.sleep(0.01) - self.target.write(json.dumps("Init").encode("ascii")) - # Read back device ID from device. - return self.read_response(max_tries=30) - - def crypto_shadow_reg_access(self) -> None: - """ Starts the crypto.fi.shadow_reg_access test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # ShadowRegAccess command. - time.sleep(0.01) - self.target.write(json.dumps("ShadowRegAccess").encode("ascii")) - - def crypto_aes_key(self) -> None: - """ Starts the crypto.fi.aes_key test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Aes command. - time.sleep(0.01) - self.target.write(json.dumps("Aes").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": True, "plaintext_trigger": False, - "encrypt_trigger": False, "ciphertext_trigger": False} - self.target.write(json.dumps(mode).encode("ascii")) - - def crypto_aes_plaintext(self) -> None: - """ Starts the crypto.fi.aes_plaintext test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Aes command. - time.sleep(0.01) - self.target.write(json.dumps("Aes").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": False, "plaintext_trigger": True, - "encrypt_trigger": False, "ciphertext_trigger": False} - self.target.write(json.dumps(mode).encode("ascii")) +from target.communication.otfi import OTFI +from target.communication.otfi_test import OTFITest - def crypto_aes_encrypt(self) -> None: - """ Starts the crypto.fi.aes_encrypt test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Aes command. - time.sleep(0.01) - self.target.write(json.dumps("Aes").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": False, "plaintext_trigger": False, - "encrypt_trigger": True, "ciphertext_trigger": False} - self.target.write(json.dumps(mode).encode("ascii")) - def crypto_aes_ciphertext(self) -> None: - """ Starts the crypto.fi.aes_ciphertext test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Aes command. - time.sleep(0.01) - self.target.write(json.dumps("Aes").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": False, "plaintext_trigger": False, - "encrypt_trigger": False, "ciphertext_trigger": True} - self.target.write(json.dumps(mode).encode("ascii")) +MODES = { + "aes": { + "key_trigger": False, "plaintext_trigger": False, + "encrypt_trigger": False, "ciphertext_trigger": False + }, + "kmac": { + "key_trigger": False, "absorb_trigger": False, + "static_trigger": False, "squeeze_trigger": False + }, +} - def crypto_kmac_key(self) -> None: - """ Starts the crypto.fi.kmac_key test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Kmac command. - time.sleep(0.01) - self.target.write(json.dumps("Kmac").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": True, "absorb_trigger": False, - "static_trigger": False, "squeeze_trigger": False} - self.target.write(json.dumps(mode).encode("ascii")) - def crypto_kmac_absorb(self) -> None: - """ Starts the crypto.fi.kmac_absorb test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Kmac command. - time.sleep(0.01) - self.target.write(json.dumps("Kmac").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": False, "absorb_trigger": True, - "static_trigger": False, "squeeze_trigger": False} - self.target.write(json.dumps(mode).encode("ascii")) +def _get_mode(ip, mode_id): + assert ip in MODES, f"IP {ip} not in MODES ({MODES})" + mode = copy.deepcopy(MODES[ip]) + assert mode_id in mode, f"Mode id {mode_id} not in {ip} mode ({mode})" + mode[mode_id] = True + return mode - def crypto_kmac_squeeze(self) -> None: - """ Starts the crypto.fi.kmac_squeeze test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Kmac command. - time.sleep(0.01) - self.target.write(json.dumps("Kmac").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": False, "absorb_trigger": False, - "static_trigger": False, "squeeze_trigger": True} - self.target.write(json.dumps(mode).encode("ascii")) - def crypto_kmac_static(self) -> None: - """ Starts the crypto.fi.kmac_static test. - """ - # CryptoFi command. - self._ujson_crypto_cmd() - # Kmac command. - time.sleep(0.01) - self.target.write(json.dumps("Kmac").encode("ascii")) - # Mode payload. - time.sleep(0.01) - mode = {"key_trigger": False, "absorb_trigger": False, - "static_trigger": True, "squeeze_trigger": False} - self.target.write(json.dumps(mode).encode("ascii")) +class OTFICrypto(OTFI): + TESTS = [ + OTFITest("shadow_reg_access"), + OTFITest("aes_key", "Aes", _get_mode("aes", "key_trigger")), + OTFITest("aes_plaintext", "Aes", _get_mode("aes", "plaintext_trigger")), + OTFITest("aes_encrypt", "Aes", _get_mode("aes", "encrypt_trigger")), + OTFITest("aes_ciphertext", "Aes", _get_mode("aes", "ciphertext_trigger")), + OTFITest("kmac_key", "Kmac", _get_mode("kmac", "key_trigger")), + OTFITest("kmac_absorb", "Kmac", _get_mode("kmac", "absorb_trigger")), + OTFITest("kmac_static", "Kmac", _get_mode("kmac", "static_trigger")), + OTFITest("kmac_squeeze", "Kmac", _get_mode("kmac", "squeeze_trigger")), + ] - def start_test(self, cfg: dict) -> None: - """ Start the selected test. - - Call the function selected in the config file. Uses the getattr() - construct to call the function. - - Args: - cfg: Config dict containing the selected test. - """ - test_function = getattr(self, cfg["test"]["which_test"]) - test_function() - - def read_response(self, max_tries: Optional[int] = 1) -> str: - """ Read response from Crypto FI framework. - Args: - max_tries: Maximum number of attempts to read from UART. - - Returns: - The JSON response of OpenTitan. - """ - it = 0 - while it != max_tries: - read_line = str(self.target.readline()) - if "RESP_OK" in read_line: - return read_line.split("RESP_OK:")[1].split(" CRC:")[0] - it += 1 - return "" + def __init__(self, target) -> None: + super().__init__(target, "Crypto") diff --git a/target/communication/fi_ibex_commands.py b/target/communication/fi_ibex_commands.py index 8785bae0..e69e6544 100644 --- a/target/communication/fi_ibex_commands.py +++ b/target/communication/fi_ibex_commands.py @@ -5,281 +5,38 @@ Communication with OpenTitan happens over the uJSON command interface. """ -import json -import time -from typing import Optional +from target.communication.otfi import OTFI +from target.communication.otfi_test import OTFITest + + +class OTFIIbex(OTFI): + TESTS = [ + OTFITest("char_unrolled_reg_op_loop"), + OTFITest("char_unrolled_mem_op_loop"), + OTFITest("char_reg_op_loop"), + OTFITest("char_mem_op_loop"), + OTFITest("char_flash_read"), + OTFITest("char_flash_write"), + OTFITest("char_sram_read"), + OTFITest("char_sram_write_static_unrolled"), + OTFITest("char_sram_write_read"), + OTFITest("char_sram_write"), + OTFITest("char_sram_static"), + OTFITest("char_conditional_branch_beq", "CharCondBranchBeq"), + OTFITest("char_conditional_branch_bne", "CharCondBranchBne"), + OTFITest("char_conditional_branch_bge", "CharCondBranchBge"), + OTFITest("char_conditional_branch_bgeu", "CharCondBranchBgeu"), + OTFITest("char_conditional_branch_blt", "CharCondBranchBlt"), + OTFITest("char_conditional_branch_bltu", "CharCondBranchBltu"), + OTFITest("char_unconditional_branch", "CharUncondBranch"), + OTFITest("char_unconditional_branch_nop", "CharUncondBranchNop"), + OTFITest("char_register_file"), + OTFITest("char_register_file_read"), + OTFITest("char_csr_write"), + OTFITest("char_csr_read"), + OTFITest("address_translation"), + OTFITest("address_translation_config", "AddressTranslationCfg"), + ] - -class OTFIIbex: def __init__(self, target) -> None: - self.target = target - - def _ujson_ibex_fi_cmd(self) -> None: - time.sleep(0.01) - self.target.write(json.dumps("IbexFi").encode("ascii")) - - def ibex_char_unrolled_reg_op_loop(self) -> None: - """ Starts the ibex.char.unrolled_reg_op_loop test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharUnrolledRegOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharUnrolledRegOpLoop").encode("ascii")) - - def ibex_char_unrolled_mem_op_loop(self) -> None: - """ Starts the ibex.char.unrolled_mem_op_loop test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharUnrolledMemOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharUnrolledMemOpLoop").encode("ascii")) - - def ibex_char_reg_op_loop(self) -> None: - """ Starts the ibex.char.reg_op_loop test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharRegOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharRegOpLoop").encode("ascii")) - - def ibex_char_mem_op_loop(self) -> None: - """ Starts the ibex.char.mem_op_loop test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharMemOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharMemOpLoop").encode("ascii")) - - def ibex_char_flash_read(self) -> None: - """ Starts the ibex.char.flash_read test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharFlashRead command. - time.sleep(0.01) - self.target.write(json.dumps("CharFlashRead").encode("ascii")) - - def ibex_char_flash_write(self) -> None: - """ Starts the ibex.char.flash_write test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharFlashWrite command. - time.sleep(0.01) - self.target.write(json.dumps("CharFlashWrite").encode("ascii")) - - def ibex_char_sram_read(self) -> None: - """ Starts the ibex.char.sram_read test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharSramRead command. - time.sleep(0.01) - self.target.write(json.dumps("CharSramRead").encode("ascii")) - - def ibex_char_sram_write_static_unrolled(self) -> None: - """ Starts the ibex.char.sram_write_static_unrolled test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharSramWriteStaticUnrolled command. - time.sleep(0.01) - self.target.write(json.dumps("CharSramWriteStaticUnrolled").encode("ascii")) - - def ibex_char_sram_write_read(self) -> None: - """ Starts the ibex.char.sram_write_read test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharSramWriteRead command. - time.sleep(0.01) - self.target.write(json.dumps("CharSramWriteRead").encode("ascii")) - - def ibex_char_sram_write(self) -> None: - """ Starts the ibex.char.sram_write test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharSramWrite command. - time.sleep(0.01) - self.target.write(json.dumps("CharSramWrite").encode("ascii")) - - def ibex_char_sram_static(self) -> None: - """ Starts the ibex.char.sram_static test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharSramWrite command. - time.sleep(0.01) - self.target.write(json.dumps("CharSramStatic").encode("ascii")) - - def ibex_char_conditional_branch_beq(self) -> None: - """ Starts the ibex.char.conditional_branch_beq test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCondBranchBeq command. - time.sleep(0.01) - self.target.write(json.dumps("CharCondBranchBeq").encode("ascii")) - - def ibex_char_conditional_branch_bne(self) -> None: - """ Starts the ibex.char.conditional_branch_bne test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCondBranchBne command. - time.sleep(0.01) - self.target.write(json.dumps("CharCondBranchBne").encode("ascii")) - - def ibex_char_conditional_branch_bge(self) -> None: - """ Starts the ibex.char.conditional_branch_bge test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCondBranchBge command. - time.sleep(0.01) - self.target.write(json.dumps("CharCondBranchBge").encode("ascii")) - - def ibex_char_conditional_branch_bgeu(self) -> None: - """ Starts the ibex.char.conditional_branch_bgeu test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCondBranchBgeu command. - time.sleep(0.01) - self.target.write(json.dumps("CharCondBranchBgeu").encode("ascii")) - - def ibex_char_conditional_branch_blt(self) -> None: - """ Starts the ibex.char.conditional_branch_blt test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCondBranchBglt command. - time.sleep(0.01) - self.target.write(json.dumps("CharCondBranchBlt").encode("ascii")) - - def ibex_char_conditional_branch_bltu(self) -> None: - """ Starts the ibex.char.conditional_branch_bltu test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCondBranchBgltu command. - time.sleep(0.01) - self.target.write(json.dumps("CharCondBranchBltu").encode("ascii")) - - def ibex_char_unconditional_branch(self) -> None: - """ Starts the ibex.char.unconditional_branch test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharUncondBranch command. - time.sleep(0.01) - self.target.write(json.dumps("CharUncondBranch").encode("ascii")) - - def ibex_char_unconditional_branch_nop(self) -> None: - """ Starts the ibex.char.unconditional_branch_nop test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharUncondBranchNop command. - time.sleep(0.01) - self.target.write(json.dumps("CharUncondBranchNop").encode("ascii")) - - def ibex_char_register_file(self) -> None: - """ Starts the ibex.char.register_file test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharRegisterFile command. - time.sleep(0.01) - self.target.write(json.dumps("CharRegisterFile").encode("ascii")) - - def ibex_char_register_file_read(self) -> None: - """ Starts the ibex.char.register_file_read test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharRegisterFileRead command. - time.sleep(0.01) - self.target.write(json.dumps("CharRegisterFileRead").encode("ascii")) - - def init(self) -> list: - """ Initialize the Ibex FI code on the chip. - Returns: - The device ID of the device. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # InitTrigger command. - time.sleep(0.01) - self.target.write(json.dumps("Init").encode("ascii")) - # Read back device ID from device. - return self.read_response(max_tries=30) - - def start_test(self, cfg: dict) -> None: - """ Start the selected test. - - Call the function selected in the config file. Uses the getattr() - construct to call the function. - - Args: - cfg: Config dict containing the selected test. - """ - test_function = getattr(self, cfg["test"]["which_test"]) - test_function() - - def ibex_char_csr_write(self) -> None: - """ Starts the ibex.fi.char.csr_write test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCsrWrite command. - time.sleep(0.01) - self.target.write(json.dumps("CharCsrWrite").encode("ascii")) - - def ibex_char_csr_read(self) -> None: - """ Starts the ibex.fi.char.csr_read test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # CharCsrRead command. - time.sleep(0.01) - self.target.write(json.dumps("CharCsrRead").encode("ascii")) - - def ibex_address_translation_config(self) -> None: - """ Starts the ibex.fi.address_translation_config test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # AddressTranslationCfg command. - time.sleep(0.01) - self.target.write(json.dumps("AddressTranslationCfg").encode("ascii")) - - def ibex_address_translation(self) -> None: - """ Starts the ibex.fi.address_translation test. - """ - # IbexFi command. - self._ujson_ibex_fi_cmd() - # AddressTranslation command. - time.sleep(0.01) - self.target.write(json.dumps("AddressTranslation").encode("ascii")) - - def read_response(self, max_tries: Optional[int] = 1) -> str: - """ Read response from Ibex FI framework. - Args: - max_tries: Maximum number of attempts to read from UART. - - Returns: - The JSON response of OpenTitan. - """ - it = 0 - while it != max_tries: - read_line = str(self.target.readline()) - if "RESP_OK" in read_line: - return read_line.split("RESP_OK:")[1].split(" CRC:")[0] - it += 1 - return "" + super().__init__(target, "Ibex") diff --git a/target/communication/fi_otbn_commands.py b/target/communication/fi_otbn_commands.py index 371c2b35..418e6f13 100644 --- a/target/communication/fi_otbn_commands.py +++ b/target/communication/fi_otbn_commands.py @@ -10,67 +10,26 @@ from typing import Optional -class OTFIOtbn: - def __init__(self, target) -> None: - self.target = target - - def _ujson_otbn_fi_cmd(self) -> None: - time.sleep(0.01) - self.target.write(json.dumps("OtbnFi").encode("ascii")) - - def otbn_char_unrolled_reg_op_loop(self) -> None: - """ Starts the otbn.fi.char.unrolled.reg.op.loop test. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # CharUnrolledRegOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharUnrolledRegOpLoop").encode("ascii")) +from target.communication.otfi import OTFI +from target.communication.otfi_test import OTFITest - def otbn_char_unrolled_dmem_op_loop(self) -> None: - """ Starts the otbn.fi.char.unrolled.dmem.op.loop test. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # CharUnrolledDmemOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharUnrolledDmemOpLoop").encode("ascii")) - def otbn_char_hardware_reg_op_loop(self) -> None: - """ Starts the otbn.fi.char.hardware.reg.op.loop test. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # CharHardwareRegOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharHardwareRegOpLoop").encode("ascii")) +class OTFIOtbn(OTFI): + TESTS = [ + OTFITest("char_unrolled_reg_op_loop"), + OTFITest("char_unrolled_dmem_op_loop"), + OTFITest("char_hardware_reg_op_loop"), + OTFITest("char_hardware_dmem_op_loop"), + OTFITest("key_sideload"), + OTFITest("load_integrity"), + ] - def otbn_char_hardware_dmem_op_loop(self) -> None: - """ Starts the otbn.fi.char.hardware.dmem.op.loop test. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # CharMemOpLoop command. - time.sleep(0.01) - self.target.write(json.dumps("CharHardwareDmemOpLoop").encode("ascii")) - - def otbn_key_sideload(self) -> None: - """ Starts the otbn.fi.key_sideload test. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # KeySideload command. - time.sleep(0.01) - self.target.write(json.dumps("KeySideload").encode("ascii")) + def __init__(self, target) -> None: + super().__init__(target, "Otbn") - def otbn_load_integrity(self) -> None: - """ Starts the otbn.fi.load_integrity test. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # LoadIntegrity command. - time.sleep(0.01) - self.target.write(json.dumps("LoadIntegrity").encode("ascii")) + def init(self, test: Optional[str] = "") -> None: + self.init_keymgr(test) + super().init(test) def init_keymgr(self, test: str) -> None: """ Initialize the key manager on the chip. @@ -80,55 +39,14 @@ def init_keymgr(self, test: str) -> None: """ if "key_sideload" in test: # OtbnFi command. - self._ujson_otbn_fi_cmd() + self._ujson_fi_cmd() # InitTrigger command. time.sleep(0.01) self.target.write(json.dumps("InitKeyMgr").encode("ascii")) # As the init resets the chip, we need to call it again to complete # the initialization of the key manager. time.sleep(2) - self._ujson_otbn_fi_cmd() + self._ujson_fi_cmd() time.sleep(0.01) self.target.write(json.dumps("InitKeyMgr").encode("ascii")) time.sleep(2) - - def init(self) -> None: - """ Initialize the OTBN FI code on the chip. - Returns: - The device ID of the device. - """ - # OtbnFi command. - self._ujson_otbn_fi_cmd() - # Init command. - time.sleep(0.01) - self.target.write(json.dumps("Init").encode("ascii")) - # Read back device ID from device. - return self.read_response(max_tries=30) - - def start_test(self, cfg: dict) -> None: - """ Start the selected test. - - Call the function selected in the config file. Uses the getattr() - construct to call the function. - - Args: - cfg: Config dict containing the selected test. - """ - test_function = getattr(self, cfg["test"]["which_test"]) - test_function() - - def read_response(self, max_tries: Optional[int] = 1) -> str: - """ Read response from Otbn FI framework. - Args: - max_tries: Maximum number of attempts to read from UART. - - Returns: - The JSON response of OpenTitan. - """ - it = 0 - while it != max_tries: - read_line = str(self.target.readline()) - if "RESP_OK" in read_line: - return read_line.split("RESP_OK:")[1].split(" CRC:")[0] - it += 1 - return "" diff --git a/target/communication/fi_rng_commands.py b/target/communication/fi_rng_commands.py new file mode 100644 index 00000000..3c44fff0 --- /dev/null +++ b/target/communication/fi_rng_commands.py @@ -0,0 +1,43 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +"""Communication interface for OpenTitan RNG FI framework. + +Communication with OpenTitan happens over the uJSON command interface. +""" +import json +import time +from typing import Optional + + +from target.communication.otfi import OTFI +from target.communication.otfi_test import OTFITest + + +class OTFIRng(OTFI): + TESTS = [ + OTFITest("csrng_bias"), + OTFITest("edn_bus_ack"), + ] + + def __init__(self, target) -> None: + super().__init__(target, "Rng") + + def init(self, test: Optional[str] = "") -> None: + """ Initialize the RNG FI code on the chip. + + Args: + test: The selected test. + Returns: + The device ID of the device. + """ + # RngFi command. + self._ujson_fi_cmd() + # Init command. + time.sleep(0.01) + if "csrng" in test: + self.target.write(json.dumps("CsrngInit").encode("ascii")) + else: + self.target.write(json.dumps("EdnInit").encode("ascii")) + # Read back device ID from device. + return self.read_response(max_tries=30) diff --git a/target/communication/otfi.py b/target/communication/otfi.py new file mode 100644 index 00000000..99c23a71 --- /dev/null +++ b/target/communication/otfi.py @@ -0,0 +1,85 @@ +"""Base class of communication interface for OpenTitan FI framework. + +Communication with OpenTitan happens over the uJSON command interface. +""" +import json +import time +from typing import Optional + + +from target.communication.otfi_test import OTFITest + + +class OTFI: + TESTS = [] + IP = ["Ibex", "Otbn", "Crypto", "Rng"] + + def __init__(self, target, ip) -> None: + self.target = target + self.ip = ip + + assert self.ip in OTFI.IP, f"ip ({self.ip} not in OTFI.IP ({OTFI.IP})" + + def _ujson_fi_cmd(self) -> None: + time.sleep(0.01) + self.target.write(json.dumps(f"{self.ip}Fi").encode("ascii")) + time.sleep(0.01) + + def init(self, test: Optional[str] = "") -> None: + """Initialize the FI code on the chip. + Returns: + The device ID of the device. + """ + # Fi command. + self._ujson_fi_cmd() + # Init command. + time.sleep(0.01) + self.target.write(json.dumps("Init").encode("ascii")) + # Read back device ID from device. + return self.read_response(max_tries=30) + + def start_test( + self, cfg: Optional[dict] = {}, testname: Optional[str] = "" + ) -> None: + """Start the selected test. + + Call the function selected in the config file. Uses the getattr() + construct to call the function. + + Args: + cfg: Config dict containing the selected test. + """ + if cfg != {}: + testname = cfg["test"]["which_test"] + testname = testname.replace(f"{self.ip.lower()}_", "", 1) + tests = [test for test in self.TESTS if test.name == testname] + assert not len(tests) == 0, f"{testname} not found in {self.TESTS}" + assert len(tests) == 1, f"Test duplicates with name {testname}" + self._run_test(tests[0]) + + def _run_test(self, test: OTFITest) -> None: + # OTFIx Fi command. + self._ujson_fi_cmd() + # Test command. + time.sleep(0.01) + self.target.write(json.dumps(test.cmd).encode("ascii")) + # Test mode. + if test.mode is not None: + time.sleep(0.01) + self.target.write(json.dumps(test.mode).encode("ascii")) + + def read_response(self, max_tries: Optional[int] = 1) -> str: + """Read response from Crypto FI framework. + Args: + max_tries: Maximum number of attempts to read from UART. + + Returns: + The JSON response of OpenTitan. + """ + it = 0 + while it != max_tries: + read_line = str(self.target.readline()) + if "RESP_OK" in read_line: + return read_line.split("RESP_OK:")[1].split(" CRC:")[0] + it += 1 + return "" diff --git a/target/communication/otfi_test.py b/target/communication/otfi_test.py new file mode 100644 index 00000000..7d250093 --- /dev/null +++ b/target/communication/otfi_test.py @@ -0,0 +1,9 @@ +"""Class of tests for OpenTitan FI framework. +""" + + +class OTFITest: + def __init__(self, name, cmd=None, mode=None): + self.name = name + self.cmd = "".join(s.capitalize() for s in name.split("_")) if cmd is None else cmd + self.mode = mode