From 72dbde35ced59da8405789ddf074696c9448fa09 Mon Sep 17 00:00:00 2001 From: Pascal Nasahl Date: Wed, 27 Mar 2024 15:39:30 +0100 Subject: [PATCH] Batch mode for Ibex SCA & new FI test This commit adds the command handler for: - ibex_sca_tl_write_batch_fvsr - ibex_sca_tl_write_batch_fvsr_fix_address - ibex_sca_tl_write_batch_random - ibex_sca_tl_write_batch_random_fix_address - ibex_sca_tl_write_fvsr - ibex_sca_tl_write_random - ibex_sca_tl_read_batch_fvsr - ibex_sca_tl_read_batch_fvsr_fix_address - ibex_sca_tl_read_batch_random - ibex_sca_tl_read_batch_random_fix_address - ibex_sca_tl_read_fvsr - ibex_sca_tl_read_random - ibex_sca_register_file_write_batch_fvsr - ibex_sca_register_file_write_batch_random - ibex_sca_register_file_write_fvsr - ibex_sca_register_file_write_random - ibex_sca_register_file_read_batch_fvsr - ibex_sca_register_file_read_batch_random - ibex_sca_register_file_read_fvsr - ibex_sca_register_file_read_random - ibex_fi_char_sram_write The device code is located in lowRISC/opentitan#22272 Signed-off-by: Pascal Nasahl --- capture/capture_ibex.py | 151 ++++++--- capture/configs/ibex_sca_chip.yaml | 39 ++- capture/configs/ibex_sca_cw310.yaml | 30 +- ci/cfg/ci_ibex_sca_cw310_ujson.yaml | 13 +- ...global_fi.ibex.char.sram_static.cw310.yaml | 39 +++ objs/sca_ujson_fpga_cw310.bin | 4 +- target/communication/fi_ibex_commands.py | 9 + target/communication/sca_ibex_commands.py | 293 ++++++++++++++++-- 8 files changed, 480 insertions(+), 98 deletions(-) create mode 100644 fault_injection/configs/pen.global_fi.ibex.char.sram_static.cw310.yaml diff --git a/capture/capture_ibex.py b/capture/capture_ibex.py index c1852d732..69a1d414b 100755 --- a/capture/capture_ibex.py +++ b/capture/capture_ibex.py @@ -22,6 +22,7 @@ import util.helpers as helpers from target.communication.sca_ibex_commands import OTIbex +from target.communication.sca_prng_commands import OTPRNG from target.communication.sca_trigger_commands import OTTRIGGER from target.targets import Target, TargetConfig from util import check_version, plot @@ -54,11 +55,12 @@ def abort_handler_during_loop(this_project, sig, frame): class CaptureConfig: """ Configuration class for the current capture. """ - capture_mode: str + test_mode: str num_traces: int num_segments: int protocol: str port: Optional[str] = "None" + batch_prng_seed: Optional[str] = "None" def setup(cfg: dict, project: Path): @@ -104,10 +106,15 @@ def setup(cfg: dict, project: Path): logger.info(f"Initializing scope {scope_type} with a sampling rate of {cfg[scope_type]['sampling_rate']}...") # noqa: E501 + # Determine if we are in batch mode or not. + batch = False + if "batch" in cfg["test"]["which_test"]: + batch = True + # Create scope config & setup scope. scope_cfg = ScopeConfig( scope_type = scope_type, - batch_mode = True, + batch_mode = batch, bit = cfg[scope_type].get("bit"), acqu_channel = cfg[scope_type].get("channel"), ip = cfg[scope_type].get("waverunner_ip"), @@ -143,38 +150,51 @@ def establish_communication(target, capture_cfg: CaptureConfig): Returns: ot_ibex: The communication interface to the Ibex SCA application. + ot_prng: The communication interface to the PRNG SCA application. ot_trig: The communication interface to the SCA trigger. """ # Create communication interface to OT Ibex. ot_ibex = OTIbex(target=target, protocol=capture_cfg.protocol) + # Create communication interface to OT PRNG. + ot_prng = OTPRNG(target=target, protocol=capture_cfg.protocol) + # Create communication interface to SCA trigger. ot_trig = OTTRIGGER(target=target, protocol=capture_cfg.protocol) - return ot_ibex, ot_trig + return ot_ibex, ot_prng, ot_trig -def generate_data(): +def generate_data(test_mode, num_data): """ Returns data used by the test. Either a fixed dataset or a random one is generated. Returns: - data: Data used by the test. + data: The data set used for the test. + data_fixed: The fixed data set. """ - fixed_data = random.randint(0, 1) - if fixed_data: - data = [0xDEADBEEF, 0xCDCDCDCD, 0xABADCAFE, 0x8BADF00D, 0xFDFDFDFD, - 0xA5A5A5A5, 0xABABABAB, 0xC00010FF] - else: - data = [] - for i in range(0, 8): - data.append(random.randint(0, 65535)) - return data - - -def capture(scope: Scope, ot_ibex: OTIbex, capture_cfg: CaptureConfig, - project: SCAProject, target: Target): + data = [] + data_fixed = 0xABBABABE + # First sample is always fixed. + sample_fixed = True + for i in range(num_data): + if "fvsr" in test_mode: + if sample_fixed: + data.append(data_fixed) + else: + data.append(random.getrandbits(32)) + sample_fixed = random.getrandbits(32) & 0x1 + elif "random" in test_mode: + tmp = random.getrandbits(32) + data.append(tmp) + else: + raise RuntimeError("Error: Invalid test mode!") + return data, data_fixed + + +def capture(scope: Scope, ot_ibex: OTIbex, ot_prng: OTPRNG, + capture_cfg: CaptureConfig, project: SCAProject, target: Target): """ Capture power consumption during execution of Ibex SCA penetration tests. Supports the following captures: @@ -185,7 +205,8 @@ def capture(scope: Scope, ot_ibex: OTIbex, capture_cfg: CaptureConfig, Args: scope: The scope class representing a scope (Husky or WaveRunner). - ot_ibex: The OpenTitan AES communication interface. + ot_ibex: The communication interface to the Ibex SCA application. + ot_prng: The communication interface to the PRNG SCA application. capture_cfg: The configuration of the capture. project: The SCA project. target: The OpenTitan target. @@ -194,6 +215,14 @@ def capture(scope: Scope, ot_ibex: OTIbex, capture_cfg: CaptureConfig, # Optimization for CW trace library. num_segments_storage = 1 + # Seed the PRNG used for generating random data. + if "batch" in capture_cfg.test_mode: + # Seed host's PRNG. + random.seed(capture_cfg.batch_prng_seed) + + # Seed the target's PRNG. + ot_prng.seed_prng(capture_cfg.batch_prng_seed.to_bytes(4, "little")) + # Register ctrl-c handler to store traces on abort. signal.signal(signal.SIGINT, partial(abort_handler_during_loop, project)) # Main capture with progress bar. @@ -202,34 +231,65 @@ def capture(scope: Scope, ot_ibex: OTIbex, capture_cfg: CaptureConfig, while remaining_num_traces > 0: # Arm the scope. scope.arm() - data = generate_data() - if capture_cfg.capture_mode == "ibex.sca.register_file_read": - ot_ibex.register_file_read(capture_cfg.num_segments, data) - elif capture_cfg.capture_mode == "ibex.sca.register_file_write": - ot_ibex.register_file_write(capture_cfg.num_segments, data) - elif capture_cfg.capture_mode == "ibex.sca.tl_read": - ot_ibex.tl_read(capture_cfg.num_segments, data) - elif capture_cfg.capture_mode == "ibex.sca.tl_write": - ot_ibex.tl_write(capture_cfg.num_segments, data) + if "batch" in capture_cfg.test_mode: + num_data = capture_cfg.num_segments + else: + # In non-batch mode, 8 uint32 values are used. + num_data = 8 + # Generate data set used for the test. + data, data_fixed = generate_data(capture_cfg.test_mode, num_data) + # Start the test based on the mode. + if "batch" in capture_cfg.test_mode: + if "fvsr" in capture_cfg.test_mode: + # In FvsR batch, the fixed dataset and the number of segments + # is transferred to the device. The rest of the dataset is + # generated on the device. Trigger is set number of segments. + ot_ibex.start_test(capture_cfg.test_mode, data_fixed, capture_cfg.num_segments) + elif "random" in capture_cfg.test_mode: + # In Random batch, number of segments is transferred to the + # device. number of segments random datasets are generated + # on the device. Trigger is set number of segments. + ot_ibex.start_test(capture_cfg.test_mode, + capture_cfg.num_segments) + else: + # In the non-batch mode, the dataset is generated in ot-sca and + # transferred to the device. Trigger is set once. + ot_ibex.start_test(capture_cfg.test_mode, data) # Capture traces. waves = scope.capture_and_transfer_waves(target) assert waves.shape[0] == capture_cfg.num_segments - # Convert data into bytearray for storage in database. - data_bytes = [] - for d in data: - data_bytes.append(d.to_bytes(4, "little")) + response = ot_ibex.ibex_sca_read_response() + # Check response. 0 for non-batch and the last data element in + # batch mode. + if "batch" in capture_cfg.test_mode: + assert response == data[-1] + else: + assert response == 0 # Store traces. - for i in range(capture_cfg.num_segments): - # Sanity check retrieved data (wave). - assert len(waves[i, :]) >= 1 - # Store trace into database. - project.append_trace(wave = waves[i, :], - plaintext = b''.join(data_bytes), - ciphertext = None, - key = None) + if "batch" in capture_cfg.test_mode: + for i in range(capture_cfg.num_segments): + # Sanity check retrieved data (wave). + assert len(waves[i, :]) >= 1 + # Store trace into database. + project.append_trace(wave = waves[i, :], + plaintext = data[i].to_bytes(4, 'little'), + ciphertext = None, + key = None) + else: + # Convert data into bytearray for storage in database. + data_bytes = [] + for d in data: + data_bytes.append(d.to_bytes(4, "little")) + # Sanity check retrieved data (wave). + assert len(waves[0, :]) >= 1 + # Store trace into database. + project.append_trace(wave = waves[0, :], + plaintext = b''.join(data_bytes), + ciphertext = None, + key = None) # Memory allocation optimization for CW trace library. num_segments_storage = project.optimize_capture(num_segments_storage) @@ -279,21 +339,22 @@ def main(argv=None): target, scope, project = setup(cfg, args.project) # Create capture config object. - capture_cfg = CaptureConfig(capture_mode = cfg["test"]["which_test"], + capture_cfg = CaptureConfig(test_mode = cfg["test"]["which_test"], num_traces = cfg["capture"]["num_traces"], num_segments = scope.scope_cfg.num_segments, protocol = cfg["target"]["protocol"], - port = cfg["target"].get("port")) - logger.info(f"Setting up capture {capture_cfg.capture_mode}...") + port = cfg["target"].get("port"), + batch_prng_seed = cfg["test"].get("batch_prng_seed")) + logger.info(f"Setting up capture {capture_cfg.test_mode}...") # Open communication with target. - ot_ibex, ot_trig = establish_communication(target, capture_cfg) + ot_ibex, ot_prng, ot_trig = establish_communication(target, capture_cfg) # Configure SW trigger. ot_trig.select_trigger(1) # Capture traces. - capture(scope, ot_ibex, capture_cfg, project, target) + capture(scope, ot_ibex, ot_prng, capture_cfg, project, target) # Print plot. print_plot(project, cfg, args.project) diff --git a/capture/configs/ibex_sca_chip.yaml b/capture/configs/ibex_sca_chip.yaml index 9b1c73481..681ea7954 100644 --- a/capture/configs/ibex_sca_chip.yaml +++ b/capture/configs/ibex_sca_chip.yaml @@ -7,25 +7,46 @@ target: output_len_bytes: 16 protocol: "ujson" port: "/dev/ttyUSB1" + usb_serial: "205F355C3236" waverunner: waverunner_ip: 192.168.33.128 - num_segments: 20 + num_segments: 1 # cycles will only be used if not given in samples num_cycles: 100 - offset_cycles: 0 + # Do not capture 100 nops that are inserted to give the trigger time to rise. + offset_cycles: 100 # sampling rate needed for cycle to sample conversion sampling_rate: 2500000000 channel: C1 sparsing: 0 capture: scope_select: waverunner - num_traces: 5000 + num_traces: 101 show_plot: True - plot_traces: 100 + plot_traces: 20 trace_db: ot_trace_library - trace_threshold: 10000 + trace_threshold: 50 test: - # which_test: ibex.sca.tl_write - # which_test: ibex.sca.tl_read - # which_test: ibex.sca.register_file_write - which_test: ibex.sca.register_file_read + # which_test: ibex_sca_tl_write_batch_fvsr + # which_test: ibex_sca_tl_write_batch_fvsr_fix_address + # which_test: ibex_sca_tl_write_batch_random + # which_test: ibex_sca_tl_write_batch_random_fix_address + # which_test: ibex_sca_tl_write_fvsr + # which_test: ibex_sca_tl_write_random + # which_test: ibex_sca_tl_read_batch_fvsr + # which_test: ibex_sca_tl_read_batch_fvsr_fix_address + # which_test: ibex_sca_tl_read_batch_random + # which_test: ibex_sca_tl_read_batch_random_fix_address + # which_test: ibex_sca_tl_read_fvsr + # which_test: ibex_sca_tl_read_random + # which_test: ibex_sca_register_file_write_batch_fvsr + # which_test: ibex_sca_register_file_write_batch_random + # which_test: ibex_sca_register_file_write_fvsr + # which_test: ibex_sca_register_file_write_random + # which_test: ibex_sca_register_file_read_batch_fvsr + # which_test: ibex_sca_register_file_read_batch_random + # which_test: ibex_sca_register_file_read_fvsr + which_test: ibex_sca_register_file_read_random + # seed for PRNG to generate sequence of plaintexts and keys; Python random + # class on host, Mersenne twister implementation on OT SW. + batch_prng_seed: 0 diff --git a/capture/configs/ibex_sca_cw310.yaml b/capture/configs/ibex_sca_cw310.yaml index a8834f3a6..8fa306d04 100644 --- a/capture/configs/ibex_sca_cw310.yaml +++ b/capture/configs/ibex_sca_cw310.yaml @@ -12,7 +12,8 @@ husky: samling_rate: 200000000 num_segments: 20 num_cycles: 100 - offset_cycles: 0 + # Do not capture 100 nops that are inserted to give the trigger time to rise. + offset_cycles: 100 scope_gain: 27 capture: scope_select: husky @@ -22,7 +23,26 @@ capture: trace_db: ot_trace_library trace_threshold: 10000 test: - # which_test: ibex.sca.tl_write - # which_test: ibex.sca.tl_read - # which_test: ibex.sca.register_file_write - which_test: ibex.sca.register_file_read + # which_test: ibex_sca_tl_write_batch_fvsr + # which_test: ibex_sca_tl_write_batch_fvsr_fix_address + # which_test: ibex_sca_tl_write_batch_random + # which_test: ibex_sca_tl_write_batch_random_fix_address + # which_test: ibex_sca_tl_write_fvsr + # which_test: ibex_sca_tl_write_random + # which_test: ibex_sca_tl_read_batch_fvsr + # which_test: ibex_sca_tl_read_batch_fvsr_fix_address + # which_test: ibex_sca_tl_read_batch_random + # which_test: ibex_sca_tl_read_batch_random_fix_address + # which_test: ibex_sca_tl_read_fvsr + # which_test: ibex_sca_tl_read_random + # which_test: ibex_sca_register_file_write_batch_fvsr + # which_test: ibex_sca_register_file_write_batch_random + # which_test: ibex_sca_register_file_write_fvsr + # which_test: ibex_sca_register_file_write_random + # which_test: ibex_sca_register_file_read_batch_fvsr + # which_test: ibex_sca_register_file_read_batch_random + # which_test: ibex_sca_register_file_read_fvsr + which_test: ibex_sca_register_file_read_random + # seed for PRNG to generate sequence of plaintexts and keys; Python random + # class on host, Mersenne twister implementation on OT SW. + batch_prng_seed: 0 \ No newline at end of file diff --git a/ci/cfg/ci_ibex_sca_cw310_ujson.yaml b/ci/cfg/ci_ibex_sca_cw310_ujson.yaml index 0c60a7478..88da6bdd2 100644 --- a/ci/cfg/ci_ibex_sca_cw310_ujson.yaml +++ b/ci/cfg/ci_ibex_sca_cw310_ujson.yaml @@ -10,19 +10,16 @@ target: port: "/dev/ttyACM_CW310_1" husky: samling_rate: 200000000 - num_segments: 20 - num_cycles: 100 - offset_cycles: 0 + num_segments: 1 + num_cycles: 50 + offset_cycles: 100 scope_gain: 27 capture: scope_select: husky - num_traces: 100 + num_traces: 40 show_plot: True plot_traces: 20 trace_db: ot_trace_library trace_threshold: 10000 test: - # which_test: ibex.sca.tl_write - # which_test: ibex.sca.tl_read - # which_test: ibex.sca.register_file_write - which_test: ibex.sca.register_file_read + which_test: ibex_sca_register_file_read_random diff --git a/fault_injection/configs/pen.global_fi.ibex.char.sram_static.cw310.yaml b/fault_injection/configs/pen.global_fi.ibex.char.sram_static.cw310.yaml new file mode 100644 index 000000000..b51dbfba8 --- /dev/null +++ b/fault_injection/configs/pen.global_fi.ibex.char.sram_static.cw310.yaml @@ -0,0 +1,39 @@ +target: + target_type: cw310 + fpga_bitstream: "../objs/lowrisc_systems_chip_earlgrey_cw310_0.1.bit" + force_program_bitstream: False + fw_bin: "../objs/sca_ujson_fpga_cw310.bin" + output_len_bytes: 16 + target_clk_mult: 0.24 + target_freq: 24000000 + baudrate: 115200 + protocol: "ujson" + port: "/dev/ttyACM4" +fisetup: + fi_gear: "husky" + fi_type: "voltage_glitch" + parameter_generation: "random" + # Voltage glitch width in cycles. + glitch_width_min: 5 + glitch_width_max: 150 + glitch_width_step: 3 + # Range for trigger delay in cycles. + trigger_delay_min: 0 + trigger_delay_max: 500 + trigger_step: 10 + # Number of iterations for the parameter sweep. + num_iterations: 100 +fiproject: + # Project database type and memory threshold. + project_db: "ot_fi_project" + project_mem_threshold: 10000 + # Store FI plot. + show_plot: True + num_plots: 10 + plot_x_axis: "trigger_delay" + plot_x_axis_legend: "[cycles]" + plot_y_axis: "glitch_width" + plot_y_axis_legend: "[cycles]" +test: + which_test: "ibex_char_sram_write" + expected_result: '{"err_status":0,"addresses":[0,0,0,0,0,0,0,0]}' diff --git a/objs/sca_ujson_fpga_cw310.bin b/objs/sca_ujson_fpga_cw310.bin index 7fb91096d..ade0b3361 100644 --- a/objs/sca_ujson_fpga_cw310.bin +++ b/objs/sca_ujson_fpga_cw310.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b79fa7125b748b00bc231ecb563e266edf4213eb27ef2318b35315d5c32169b1 -size 304188 +oid sha256:3ba81bd5ab2f24b760f676555abf169af196e0e0ebe17f48588dca252e486d0a +size 324028 diff --git a/target/communication/fi_ibex_commands.py b/target/communication/fi_ibex_commands.py index 28e5f6e30..4ae54d558 100644 --- a/target/communication/fi_ibex_commands.py +++ b/target/communication/fi_ibex_commands.py @@ -89,6 +89,15 @@ def ibex_char_sram_write(self) -> None: # 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_unconditional_branch(self) -> None: """ Starts the ibex.char.unconditional_branch test. diff --git a/target/communication/sca_ibex_commands.py b/target/communication/sca_ibex_commands.py index 2b08d785e..3a821cb7d 100644 --- a/target/communication/sca_ibex_commands.py +++ b/target/communication/sca_ibex_commands.py @@ -22,8 +22,9 @@ def _ujson_ibex_sca_cmd(self): time.sleep(0.01) self.target.write(json.dumps("IbexSca").encode("ascii")) - def _ujson_ibex_sca_ack(self, num_attempts: Optional[int] = 100): - # Wait for ack. + def ibex_sca_read_response(self, num_attempts: Optional[int] = 100): + """ Reads back the "result" response from the device. + """ read_counter = 0 while read_counter < num_attempts: read_line = str(self.target.readline()) @@ -31,10 +32,7 @@ def _ujson_ibex_sca_ack(self, num_attempts: Optional[int] = 100): json_string = read_line.split("RESP_OK:")[1].split(" CRC:")[0] try: if "result" in json_string: - status = json.loads(json_string)["result"] - if status != 0: - raise Exception("Acknowledge error: Device and host not in sync") - return status + return json.loads(json_string)["result"] except Exception: raise Exception("Acknowledge error: Device and host not in sync") else: @@ -49,10 +47,23 @@ def init(self): # Init the Ibex SCA tests. self.target.write(json.dumps("Init").encode("ascii")) - def register_file_read(self, num_iterations: int, data: list[int]): - """ Start ibex.sca.register_file_read test. + def ibex_sca_register_file_read_batch_random(self, num_segments: int): + """ Start ibex.sca.register_file_read_batch_random test. + Args: + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # RFReadBatchRandom command. + self.target.write(json.dumps("RFReadBatchRandom").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_register_file_read_random(self, data: list[int]): + """ Start ibex.sca.register_file_read_random test. Args: - num_iterations: The number of iterations the RF is read. data: The data that is first written into the RF and then read back. """ # IbexSca command. @@ -61,16 +72,84 @@ def register_file_read(self, num_iterations: int, data: list[int]): self.target.write(json.dumps("RFRead").encode("ascii")) # Data payload. time.sleep(0.01) - data = {"num_iterations": num_iterations, "data": data} + data = {"data": data} self.target.write(json.dumps(data).encode("ascii")) - # Wait for ack. + + def ibex_sca_register_file_read_batch_fvsr(self, data: int, num_segments: int): + """ Start ibex.sca.register_file_read_batch_fvsr test. + Args: + data: The data that is first written into the RF and then read back. + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # RFReadBatchFvsr command. + self.target.write(json.dumps("RFReadBatchFvsr").encode("ascii")) + # Data payload. time.sleep(0.01) - self._ujson_ibex_sca_ack() + data = {"num_iterations": num_segments, "fixed_data": data} + self.target.write(json.dumps(data).encode("ascii")) - def register_file_write(self, num_iterations: int, data: list[int]): - """ Start ibex.sca.register_file_write test. + def ibex_sca_register_file_read_fvsr(self, data: list[int]): + """ Start ibex.sca.register_file_read_fvsr test. + Args: + data: The data that is first written into the RF and then read back. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # RFRead command. + self.target.write(json.dumps("RFRead").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_register_file_write_batch_random(self, num_segments: int): + """ Start ibex.sca.register_file_write_batch_random test. + Args: + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # RFWriteBatchRandom command. + self.target.write(json.dumps("RFWriteBatchRandom").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_register_file_write_random(self, data: list[int]): + """ Start ibex.sca.register_file_write_random test. + Args: + data: The data that is written into the RF. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # RFWrite command. + self.target.write(json.dumps("RFWrite").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_register_file_write_batch_fvsr(self, data: int, num_segments: int): + """ Start ibex.sca.register_file_write_batch_fvsr test. + Args: + data: The data that is written into the RF. + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # RFWriteBatchFvsr command. + self.target.write(json.dumps("RFWriteBatchFvsr").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments, "fixed_data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_register_file_write_fvsr(self, data: list[int]): + """ Start ibex.sca.register_file_write_fvsr test. Args: - num_iterations: The number of iterations the RF is written. data: The data that is written into the RF. """ # IbexSca command. @@ -79,16 +158,40 @@ def register_file_write(self, num_iterations: int, data: list[int]): self.target.write(json.dumps("RFWrite").encode("ascii")) # Data payload. time.sleep(0.01) - data = {"num_iterations": num_iterations, "data": data} + data = {"data": data} self.target.write(json.dumps(data).encode("ascii")) - # Wait for ack. + + def ibex_sca_tl_write_batch_random(self, num_segments: int): + """ Start ibex.sca.tl_write_batch_random test. + Args: + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLWriteBatchRandom command. + self.target.write(json.dumps("TLWriteBatchRandom").encode("ascii")) + # Data payload. time.sleep(0.01) - self._ujson_ibex_sca_ack() + data = {"num_iterations": num_segments} + self.target.write(json.dumps(data).encode("ascii")) - def tl_write(self, num_iterations: int, data: list[int]): - """ Start ibex.sca.tl_write test. + def ibex_sca_tl_write_batch_random_fix_address(self, num_segments: int): + """ Start ibex.sca.tl_write_batch_random_fix_address test. + Args: + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLWriteBatchRandomFixAddress command. + self.target.write(json.dumps("TLWriteBatchRandomFixAddress").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_write_random(self, data: list[int]): + """ Start ibex.sca.tl_write_random test. Args: - num_iterations: The number of iterations the RF is written. data: The data that is written into the SRAM over Tl-UL. """ # IbexSca command. @@ -97,14 +200,84 @@ def tl_write(self, num_iterations: int, data: list[int]): self.target.write(json.dumps("TLWrite").encode("ascii")) # Data payload. time.sleep(0.01) - data = {"num_iterations": num_iterations, "data": data} + data = {"data": data} self.target.write(json.dumps(data).encode("ascii")) - # Wait for ack. + + def ibex_sca_tl_write_batch_fvsr(self, data: int, num_segments: int): + """ Start ibex.sca.tl_write_batch_fvsr test. + Args: + data: The data that is written into the SRAM over Tl-UL. + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLWriteBatchFvsr command. + self.target.write(json.dumps("TLWriteBatchFvsr").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments, "fixed_data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_write_batch_fvsr_fix_address(self, data: int, + num_segments: int): + """ Start ibex.sca.tl_write_batch_fvsr_fix_address test. + Args: + data: The data that is written into the SRAM over Tl-UL. + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLWriteBatchFvsrFixAddress command. + self.target.write(json.dumps("TLWriteBatchFvsrFixAddress").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments, "fixed_data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_write_fvsr(self, data: list[int]): + """ Start ibex.sca.tl_write_fvsr test. + Args: + data: The data that is written into the SRAM over Tl-UL. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLWrite command. + self.target.write(json.dumps("TLWrite").encode("ascii")) + # Data payload. time.sleep(0.01) - self._ujson_ibex_sca_ack() + data = {"data": data} + self.target.write(json.dumps(data).encode("ascii")) - def tl_read(self, num_iterations: int, data: list[int]): - """ Start ibex.sca.tl_read test. + def ibex_sca_tl_read_batch_random(self, num_segments: int): + """ Start ibex.sca.tl_read_batch_random test. + Args: + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLReadBatchRandom command. + self.target.write(json.dumps("TLReadBatchRandom").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_read_batch_random_fix_address(self, num_segments: int): + """ Start ibex.sca.tl_read_batch_random_fix_address test. + Args: + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLReadBatchRandomFixAddress command. + self.target.write(json.dumps("TLReadBatchRandomFixAddress").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_read_random(self, data: list[int]): + """ Start ibex.sca.tl_read_random test. Args: num_iterations: The number of iterations the RF is written. data: The data that is written into the SRAM over Tl-UL. @@ -115,8 +288,70 @@ def tl_read(self, num_iterations: int, data: list[int]): self.target.write(json.dumps("TLRead").encode("ascii")) # Data payload. time.sleep(0.01) - data = {"num_iterations": num_iterations, "data": data} + data = {"data": data} self.target.write(json.dumps(data).encode("ascii")) - # Wait for ack. + + def ibex_sca_tl_read_batch_fvsr(self, data: int, num_segments: int): + """ Start ibex.sca.tl_read_batch_fvsr test. + Args: + data: The data that is written into the SRAM over Tl-UL. + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLReadBatchFvsr command. + self.target.write(json.dumps("TLReadBatchFvsr").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"num_iterations": num_segments, "fixed_data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_read_batch_fvsr_fix_address(self, data: int, + num_segments: int): + """ Start ibex.sca.tl_read_batch_fvsr_fix_address test. + Args: + data: The data that is written into the SRAM over Tl-UL. + num_segments: The number of iterations. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLReadBatchFvsrFixAddress command. + self.target.write(json.dumps("TLReadBatchFvsrFixAddress").encode("ascii")) + # Data payload. time.sleep(0.01) - self._ujson_ibex_sca_ack() + data = {"num_iterations": num_segments, "fixed_data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def ibex_sca_tl_read_fvsr(self, data: list[int]): + """ Start ibex.sca.tl_read_fvsr test. + Args: + num_iterations: The number of iterations the RF is written. + data: The data that is written into the SRAM over Tl-UL. + """ + # IbexSca command. + self._ujson_ibex_sca_cmd() + # TLRead command. + self.target.write(json.dumps("TLRead").encode("ascii")) + # Data payload. + time.sleep(0.01) + data = {"data": data} + self.target.write(json.dumps(data).encode("ascii")) + + def start_test(self, testname: str, arg1 = None, arg2 = None) -> None: + """ Start the selected test. + + Call the function selected in the config file. Uses the getattr() + construct to call the function. + + Args: + testname: The test to start + arg1: The first argument passed to the test. + arg2: The second argument passed to the test. + """ + test_function = getattr(self, testname) + if arg1 is not None and arg2 is None: + test_function(arg1) + elif arg2 is not None: + test_function(arg1, arg2) + else: + test_function()