firestarter 3.2.7b2__tar.gz → 3.3.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {firestarter-3.2.7b2/firestarter.egg-info → firestarter-3.3.0b1}/PKG-INFO +1 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/_version.py +3 -3
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/channel.py +8 -15
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/chip_resolver.py +27 -2
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/cli_handlers.py +74 -39
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/codec.py +73 -33
- firestarter-3.3.0b1/firestarter/command_codec.py +188 -0
- firestarter-3.3.0b1/firestarter/command_frame.py +105 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/constants.py +21 -28
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/data/chip_database.json +383 -262
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/data/pinouts.json +11 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/database.py +2 -2
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/diagnostic_report.py +1 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/eprom_info.py +2 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/eprom_operations.py +87 -105
- firestarter-3.3.0b1/firestarter/eprom_rows.py +12 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/exceptions.py +19 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/firmware.py +93 -14
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/frame_parser.py +7 -2
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/fw_release_gate.py +86 -15
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/hardware.py +120 -85
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/ic_layout.py +3 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/jumper_table.py +10 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/messages.py +102 -10
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/page_size_gate.py +4 -4
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/serial_comm.py +135 -80
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/vpp_display.py +8 -1
- firestarter-3.3.0b1/firestarter/write_budget.py +74 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1/firestarter.egg-info}/PKG-INFO +1 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter.egg-info/SOURCES.txt +14 -2
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter.egg-info/scm_file_list.json +14 -4
- firestarter-3.3.0b1/firestarter.egg-info/scm_version.json +8 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/__snapshots__/test_characterization.ambr +2 -2
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/conftest.py +221 -95
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fake_chip.py +4 -33
- firestarter-3.3.0b1/tests/fixtures/operator_doubles.py +114 -0
- firestarter-3.3.0b1/tests/fixtures/ready_ack.py +46 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/report_shapes.py +24 -126
- firestarter-3.3.0b1/tests/fresh_cli.py +116 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/golden/chip_database_field_inventory.json +5 -32
- firestarter-3.3.0b1/tests/golden/command_vectors.json +435 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/golden/wire_dict.json +41 -4
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/plan_corpus.py +29 -49
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_b15_page_size_corroboration.py +0 -55
- firestarter-3.3.0b1/tests/test_bandgap_back_solve.py +64 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_blast_radius_invariance.py +4 -177
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_boot_block_hint.py +7 -20
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_budget_failure_render.py +32 -89
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_bug_characterization.py +17 -24
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_build_db_inclusion.py +111 -5
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_canonical_part_number.py +5 -46
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_characterization.py +7 -31
- firestarter-3.3.0b1/tests/test_chip_database_field_inventory.py +220 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_name_resolution.py +38 -46
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_resolver.py +142 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_test.py +309 -460
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_test_blank_check_order.py +7 -12
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_test_cycle.py +17 -40
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_test_sdp_leg.py +93 -336
- firestarter-3.3.0b1/tests/test_chip_test_timing.py +145 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_chip_test_uv_slot_write.py +35 -122
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_cli_handlers.py +72 -238
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_cobs.py +2 -57
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_codec.py +7 -9
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_codec_format_message.py +81 -47
- firestarter-3.3.0b1/tests/test_command_codec.py +314 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_compare.py +86 -166
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_config.py +11 -20
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_config_band_refusal.py +96 -19
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_connect_cost_harness.py +1 -7
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_consistency_check.py +153 -210
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_coverage_floor_v18.py +6 -17
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_database_conversion.py +3 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_datasheet_overrides.py +5 -18
- firestarter-3.3.0b1/tests/test_decoder.py +475 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_derive_plan_no_drop_sweep.py +3 -109
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_derive_plan_structural_sentinel.py +53 -319
- firestarter-3.3.0b1/tests/test_dev_group_channel_gating.py +187 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_dev_test_cmd.py +40 -150
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_dev_tools_channel_gate.py +23 -7
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_devtest_firmware_error_propagation.py +4 -15
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_diagnostic_report.py +213 -384
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_dip24_2816_erase_gate.py +12 -13
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_eprom_info.py +3 -1
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_eprom_operations.py +289 -774
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_erase_blank_step_nonregression.py +4 -27
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_erase_flag_invariants.py +15 -25
- firestarter-3.3.0b1/tests/test_even_block.py +132 -0
- firestarter-3.3.0b1/tests/test_every_row_encodes.py +41 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_firmware_install.py +37 -57
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_flash4_erase_gate.py +10 -22
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fw_list_failure_vs_empty.py +0 -9
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fw_port_targeting_and_blind_install.py +12 -20
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fw_release_gate.py +248 -69
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fw_update_dead_endpoint.py +5 -4
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fw_update_path_gate.py +25 -48
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fw_version_guard.py +5 -16
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_fwguard.py +13 -32
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_hardware.py +143 -194
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_hw_revision_gate.py +12 -151
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_ic_layout.py +3 -17
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_info_vpp_and_erase.py +37 -15
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_jp5_gate.py +23 -22
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_jumper_table.py +9 -4
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_lock_status_class_partition.py +12 -14
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_lock_status_cli.py +0 -5
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_lock_status_wire.py +36 -41
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_logging_utils.py +9 -9
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_matrix_artifact.py +3 -36
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_page_size_alignment_refusal.py +37 -41
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_page_size_invariants.py +29 -65
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_page_size_write_refusal.py +29 -31
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_parse_devtest_issue.py +3 -4
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_probe_spurious_setup_ack.py +17 -30
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_programming_vcc_census.py +8 -108
- firestarter-3.3.0b1/tests/test_protect_flags_doc_measurements.py +32 -0
- firestarter-3.3.0b1/tests/test_protection_table_citations.py +56 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_protocol_not_implemented.py +2 -45
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_protocol_not_implemented_production_path.py +24 -152
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_provenance.py +0 -17
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_pulse_us_override.py +38 -118
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_py32_channel_gating.py +20 -114
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_py32_dfu.py +7 -9
- firestarter-3.3.0b1/tests/test_pyusb_api_surface.py +155 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_sdp_capability.py +0 -3
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_sdp_db_invariant.py +1 -41
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_serial_characterization.py +27 -20
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_serial_comm.py +52 -129
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_session_lease.py +7 -14
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_setup_refusals.py +13 -14
- firestarter-3.3.0b1/tests/test_setup_typed_refusals.py +109 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_submit.py +265 -897
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_transport_counters.py +38 -31
- firestarter-3.3.0b1/tests/test_unsupported_frame_fallback.py +252 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_utils.py +8 -9
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_validate_family_cmd.py +0 -6
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_validate_oracle.py +27 -137
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_validated_parts_regression_surface.py +51 -43
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_vcc_margin_rail.py +35 -49
- firestarter-3.3.0b1/tests/test_vpp_decode_table.py +50 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_vpp_rail_classification.py +3 -211
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_wire_dict_equivalence.py +11 -26
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_blank_guard.py +107 -275
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_blank_guard_pinning.py +20 -18
- firestarter-3.3.0b1/tests/test_write_budget.py +52 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_guard_port_pinning.py +18 -52
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_progress.py +56 -129
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_response_budget.py +164 -267
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_skip_erase_0x0d.py +2 -8
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_skip_sdp_unlock.py +34 -86
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_write_verify.py +77 -213
- firestarter-3.2.7b2/firestarter.egg-info/scm_version.json +0 -8
- firestarter-3.2.7b2/tests/golden/build_db_part_specific_constants.json +0 -19
- firestarter-3.2.7b2/tests/test_build_db_constant_census.py +0 -138
- firestarter-3.2.7b2/tests/test_chip_database_field_inventory.py +0 -401
- firestarter-3.2.7b2/tests/test_chip_test_timing.py +0 -243
- firestarter-3.2.7b2/tests/test_decoder.py +0 -668
- firestarter-3.2.7b2/tests/test_dev_group_channel_gating.py +0 -331
- firestarter-3.2.7b2/tests/test_even_block.py +0 -198
- firestarter-3.2.7b2/tests/test_protect_flags_doc_measurements.py +0 -298
- firestarter-3.2.7b2/tests/test_protection_table_citations.py +0 -141
- firestarter-3.2.7b2/tests/test_pyusb_api_surface.py +0 -209
- firestarter-3.2.7b2/tests/test_vpp_decode_table.py +0 -59
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/LICENSE +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/MANIFEST.in +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/README.md +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/__init__.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/address_parser.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/avr_tool.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/chip_test.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/compare.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/config.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/dip24_2816_erase_gate.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/erase_support.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/flash4_erase_gate.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/jp5_gate.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/lock_status.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/log_capture.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/logging_utils.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/main.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/protection_readability.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/py32_dfu.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/sdp_capability.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/sdp_honesty.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/submit.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/timestamps.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/transport_counters.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/utils.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter/write_blank_guard.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter.egg-info/dependency_links.txt +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter.egg-info/entry_points.txt +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter.egg-info/requires.txt +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/firestarter.egg-info/top_level.txt +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/pyproject.toml +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/setup.cfg +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/__init__.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/part_number_delta.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/at28c256-full-all-ok-sdp.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/attr01-status-axis-transport-fault.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/gh20-at28c256-fail.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/gh23-w27e257-fail.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/gh28-m27c512-fail.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/gh47-sst27sf512-pass.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/m27c512-full-all-ok.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/m27c512-full-blank-check-bad.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/m27c512-full-canonical-name.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/m27c512-full-comma-joined-name.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/m27c512-full-runs-1.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/prune03-synthesized-fingerprint-match.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/sst27sf512-full-all-ok.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/sst27sf512-six-step-readback-gated.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/sst27sf512-six-step.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/synthetic-arm4-empty-results.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/synthetic-arm4-no-ok.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/uv-slot-write-pass.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/reports/w27e257-full-all-ok.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/shape_ids.json +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/fixtures/synthetic_nonzero_chip_id.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_address_parser.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_build_db_interpret_timing.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_build_db_pinout_fork.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_build_db_sram_non_volatile.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_click_group_gate_hook.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_dfu_opcode_anchors.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_endpoint_constants.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_eprom_database.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_error_code_seam.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_extra_chips_supplement.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_log_capture.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_protection_resolution.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_protection_status_catalog.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_py32_flash_map_host.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_py32_pyusb_absent.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_sdp_honesty.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_timestamps.py +0 -0
- {firestarter-3.2.7b2 → firestarter-3.3.0b1}/tests/test_uv_mask.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firestarter
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0b1
|
|
4
4
|
Summary: EPROM programmer software for the Relatively Universal ROM Programmer
|
|
5
5
|
Author-email: Henrik Olsson <henols@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/henols/firestarter_app
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '3.
|
|
22
|
-
__version_tuple__ = version_tuple = (3,
|
|
21
|
+
__version__ = version = '3.3.0b1'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 3, 0, 'b1')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g927ad2b'
|
|
@@ -59,23 +59,16 @@ def is_prerelease_build() -> bool:
|
|
|
59
59
|
"""True when this build came off `beta` (its own version is a pre-release).
|
|
60
60
|
|
|
61
61
|
Fails **closed**: an unparseable version is treated as stable, so a gated
|
|
62
|
-
feature stays hidden rather than leaking.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
feature stays hidden rather than leaking. `_maybe_auto_route_to_pre` uses
|
|
63
|
+
this function, so it treats an unparseable version the same way. In
|
|
64
|
+
practice `__version__` is a literal in the package and is always valid
|
|
65
|
+
PEP 440; dev versions (`2.0.7_dev`) parse as pre-releases and therefore
|
|
66
|
+
keep gated features enabled while working from a checkout.
|
|
67
67
|
"""
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
import firestarter as _pkg
|
|
69
|
+
from firestarter.fw_release_gate import is_prerelease_version
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
return bool(Version(_pkg.__version__).is_prerelease)
|
|
75
|
-
except InvalidVersion:
|
|
76
|
-
return False
|
|
77
|
-
except ImportError: # pragma: no cover — packaging is a hard dependency
|
|
78
|
-
return False
|
|
71
|
+
return is_prerelease_version(_pkg.__version__) is True
|
|
79
72
|
|
|
80
73
|
|
|
81
74
|
def is_board_available(board: str | None) -> bool:
|
|
@@ -9,7 +9,7 @@ Single chokepoint between CLI dispatch and the EPROM database lookup/conversion.
|
|
|
9
9
|
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from firestarter.database import ROM_CE, ROM_OE, EpromDatabase
|
|
12
|
+
from firestarter.database import ROM_CE, ROM_OE, EpromDatabase, pin_conversions
|
|
13
13
|
from firestarter.exceptions import (
|
|
14
14
|
AmbiguousChipNameError,
|
|
15
15
|
ChipNotFoundError,
|
|
@@ -18,6 +18,13 @@ from firestarter.exceptions import (
|
|
|
18
18
|
|
|
19
19
|
_NON_ZERO_WIRE_FIELDS = ("memory-size", "pin-count", "vpp_mv")
|
|
20
20
|
|
|
21
|
+
# The 0x10 (Intel 28F) handler routes 12V to socket pin 1 (CTRL_VPP_P1_ENABLE).
|
|
22
|
+
_INTEL_28F_ALGORITHM = 0x10
|
|
23
|
+
|
|
24
|
+
# The shield supplies a fixed 5V VCC. Must agree with SHIELD_MIN_VCC_MV in
|
|
25
|
+
# tools/build_db.py.
|
|
26
|
+
_SHIELD_MIN_VCC_MV = 4500
|
|
27
|
+
|
|
21
28
|
|
|
22
29
|
def _qualified_choices(name: str, db: EpromDatabase) -> list[str]:
|
|
23
30
|
"""The row `name` selects first, then each other row it selects that
|
|
@@ -115,13 +122,22 @@ def resolve_chip(name: str, db: EpromDatabase | None = None) -> dict[str, Any]:
|
|
|
115
122
|
"a chip entry must specify a non-zero 'algorithm' to be programmed"
|
|
116
123
|
)
|
|
117
124
|
|
|
125
|
+
# A 3V part runs out of its rating on the shield's 5V supply. A user
|
|
126
|
+
# override that marks such a row supported is refused here, before any wire.
|
|
127
|
+
vcc_mv = raw_config.get("electrical", {}).get("vcc_mv")
|
|
128
|
+
if isinstance(vcc_mv, int) and 0 < vcc_mv < _SHIELD_MIN_VCC_MV:
|
|
129
|
+
raise ChipNotImplementedError(
|
|
130
|
+
f"{name}: the chip's operating VCC is {vcc_mv / 1000:g}V, but the "
|
|
131
|
+
"shield supplies 5V. The programmer refuses the chip."
|
|
132
|
+
)
|
|
133
|
+
|
|
118
134
|
full = db.get_eprom(name)
|
|
119
135
|
data = db.convert_to_programmer(full) if full else None
|
|
120
136
|
if not data:
|
|
121
137
|
raise ChipNotFoundError(name)
|
|
122
138
|
|
|
123
139
|
# The firmware refuses a memory command whose memory-size, pin-count or
|
|
124
|
-
# vpp_mv is 0,
|
|
140
|
+
# vpp_mv is 0 (MSG_ERR_BAD_COMMAND), so the port walk sees no programmer.
|
|
125
141
|
# No shipped row has a 0 here; a user database override can.
|
|
126
142
|
zero_fields = [key for key in _NON_ZERO_WIRE_FIELDS if not data.get(key)]
|
|
127
143
|
if zero_fields:
|
|
@@ -148,4 +164,13 @@ def resolve_chip(name: str, db: EpromDatabase | None = None) -> dict[str, Any]:
|
|
|
148
164
|
f"{1 << len(bus)} of its {data['memory-size']} bytes. The programmer "
|
|
149
165
|
"cannot address the rest of the chip."
|
|
150
166
|
)
|
|
167
|
+
# The 0x10 handler puts 12V on socket pin 1, so a 0x10 chip whose bus uses
|
|
168
|
+
# that pin (A18 on the SST28SF040 family) would take 12V on an address
|
|
169
|
+
# line. Only a 32-pin chip has a pin there: a smaller chip sits
|
|
170
|
+
# bottom-aligned, and none of its pins converts to the socket pin 1 line.
|
|
171
|
+
if bus and algorithm == _INTEL_28F_ALGORITHM and pin_conversions[32][1] in bus:
|
|
172
|
+
raise ChipNotImplementedError(
|
|
173
|
+
f"{name}: algorithm 0x10 puts 12V on socket pin 1, but the chip's pin "
|
|
174
|
+
"map uses pin 1 as an address line. The programmer refuses the chip."
|
|
175
|
+
)
|
|
151
176
|
return data
|
|
@@ -21,6 +21,7 @@ import click.shell_completion
|
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
|
|
23
23
|
from firestarter import __version__ as version
|
|
24
|
+
from firestarter import command_frame as cf
|
|
24
25
|
from firestarter import (
|
|
25
26
|
dip24_2816_erase_gate,
|
|
26
27
|
flash4_erase_gate,
|
|
@@ -62,6 +63,7 @@ from firestarter.chip_test import (
|
|
|
62
63
|
sdp_hold_state,
|
|
63
64
|
sdp_oracle_applicable,
|
|
64
65
|
)
|
|
66
|
+
from firestarter.command_codec import CommandEncodeError
|
|
65
67
|
from firestarter.config import ConfigManager, get_config_dir
|
|
66
68
|
from firestarter.constants import (
|
|
67
69
|
CONFIG_R1_MAX,
|
|
@@ -85,6 +87,7 @@ from firestarter.exceptions import (
|
|
|
85
87
|
ChipNotFoundError,
|
|
86
88
|
ChipNotImplementedError,
|
|
87
89
|
EpromOperationError,
|
|
90
|
+
FirmwareFrameVersionError,
|
|
88
91
|
FirmwareOperationError,
|
|
89
92
|
FirmwareOutdatedError,
|
|
90
93
|
HardwareOperationError,
|
|
@@ -98,7 +101,7 @@ from firestarter.exceptions import (
|
|
|
98
101
|
)
|
|
99
102
|
from firestarter.firmware import FIRMWARE_VERSION_RE, FirmwareManager
|
|
100
103
|
from firestarter.fw_release_gate import require_installable_release
|
|
101
|
-
from firestarter.hardware import HardwareManager
|
|
104
|
+
from firestarter.hardware import HardwareManager, bandgap_from_measured_vcc
|
|
102
105
|
from firestarter.lock_status import (
|
|
103
106
|
classify_protection_response,
|
|
104
107
|
exit_code_for_class,
|
|
@@ -238,6 +241,8 @@ def map_typed_errors(f: Callable[..., Any]) -> Callable[..., Any]:
|
|
|
238
241
|
return f(*args, **kwargs)
|
|
239
242
|
except ChipNotFoundError as e:
|
|
240
243
|
raise click.ClickException(str(e)) from e
|
|
244
|
+
except FirmwareFrameVersionError as e:
|
|
245
|
+
raise click.ClickException(f"Command frame version mismatch: {e}") from e
|
|
241
246
|
except FirmwareOutdatedError as e:
|
|
242
247
|
raise click.ClickException(f"Firmware outdated: {e}") from e
|
|
243
248
|
except (SerialError, SerialTimeoutError) as e:
|
|
@@ -269,6 +274,11 @@ def map_typed_errors(f: Callable[..., Any]) -> Callable[..., Any]:
|
|
|
269
274
|
# make this host-voiced refusal (write_blank_guard.py) read as a
|
|
270
275
|
# hardware fault instead of an input-validation refusal.
|
|
271
276
|
raise click.ClickException(str(e)) from e
|
|
277
|
+
except CommandEncodeError as e:
|
|
278
|
+
# The host refused the command before any byte reached the port.
|
|
279
|
+
# Above the EpromOperationError arm: "Programmer error" would read
|
|
280
|
+
# as a hardware fault for an input the frame cannot carry.
|
|
281
|
+
raise click.ClickException(f"Refused before sending: {e}") from e
|
|
272
282
|
except EpromOperationError as e:
|
|
273
283
|
raise click.ClickException(f"Programmer error: {e}") from e
|
|
274
284
|
except Pin1HazardRefusedError as e:
|
|
@@ -343,29 +353,15 @@ def _maybe_auto_route_to_pre(args: object) -> None:
|
|
|
343
353
|
or getattr(args, "stable", False)
|
|
344
354
|
):
|
|
345
355
|
return
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
"Beta app detected — defaulting to --pre. "
|
|
356
|
-
"Use --firmware-version X.Y.Z to pin a stable version."
|
|
357
|
-
)
|
|
358
|
-
except InvalidVersion:
|
|
359
|
-
# Deliberate fail-OPEN, and deliberately the opposite polarity to
|
|
360
|
-
# fw_release_gate, which fails CLOSED on the same unreadable input.
|
|
361
|
-
# The asymmetry is intended: the worst case here is that the wrong
|
|
362
|
-
# channel is picked, which the gate then judges on its merits; the
|
|
363
|
-
# worst case there is a board flashed with firmware this host cannot
|
|
364
|
-
# drive, recoverable only through the bootloader. Do not "align"
|
|
365
|
-
# these two without re-reading both worst cases.
|
|
366
|
-
pass
|
|
367
|
-
except ImportError:
|
|
368
|
-
pass
|
|
356
|
+
# An unreadable version is not a pre-release here: the worst case is the
|
|
357
|
+
# wrong channel, which fw_release_gate then judges and refuses (it fails
|
|
358
|
+
# CLOSED on the same input). Do not "align" the two polarities.
|
|
359
|
+
if is_prerelease_build():
|
|
360
|
+
args.pre = True # type: ignore[attr-defined]
|
|
361
|
+
helper_logger.info(
|
|
362
|
+
"Beta app detected — defaulting to --pre. "
|
|
363
|
+
"Use --firmware-version X.Y.Z to pin a stable version."
|
|
364
|
+
)
|
|
369
365
|
|
|
370
366
|
|
|
371
367
|
def _build_op_flags(
|
|
@@ -381,6 +377,7 @@ def _build_op_flags(
|
|
|
381
377
|
# SDP auto-set condition inside write() below -- flipping this default
|
|
382
378
|
# invalidates the removal-safety argument the `dev sdp` removal rests on.
|
|
383
379
|
skip_sdp_unlock: bool = False,
|
|
380
|
+
skip_final_verify: bool = False,
|
|
384
381
|
input_enable: bool | None = None,
|
|
385
382
|
chip_disable: bool | None = None,
|
|
386
383
|
) -> int:
|
|
@@ -410,6 +407,7 @@ def _build_op_flags(
|
|
|
410
407
|
verbose,
|
|
411
408
|
skip_erase=skip_erase,
|
|
412
409
|
skip_sdp_unlock=skip_sdp_unlock,
|
|
410
|
+
skip_final_verify=skip_final_verify,
|
|
413
411
|
)
|
|
414
412
|
if input_enable is not None:
|
|
415
413
|
flags |= 0 if input_enable else FLAG_OUTPUT_ENABLE
|
|
@@ -947,6 +945,9 @@ def write(
|
|
|
947
945
|
vpe_as_vpp=vpe_as_vpp,
|
|
948
946
|
skip_erase=skip_erase,
|
|
949
947
|
skip_sdp_unlock=skip_sdp_unlock,
|
|
948
|
+
# The read-back below compares every byte, so the board's own
|
|
949
|
+
# final verify pass would only repeat it.
|
|
950
|
+
skip_final_verify=verify,
|
|
950
951
|
),
|
|
951
952
|
# Translate Click's `None` ("--pulse-us not supplied") into
|
|
952
953
|
# write_eprom's own integer sentinel (0 means "use the database
|
|
@@ -1062,8 +1063,7 @@ def _address_size_refusal_exit_code(
|
|
|
1062
1063
|
"""Refuse a negative start address or a size below 1 byte before the
|
|
1063
1064
|
serial port opens.
|
|
1064
1065
|
|
|
1065
|
-
The firmware refuses both at setup,
|
|
1066
|
-
firmware too old for this CLI gives, so without this check the operator
|
|
1066
|
+
The firmware refuses both at setup, so without this check the operator
|
|
1067
1067
|
sees only "no programmer found". Returns 2 to refuse with, or `None`.
|
|
1068
1068
|
A malformed string is not refused here: `_setup_operation`'s own parse
|
|
1069
1069
|
handling still owns that error.
|
|
@@ -1606,15 +1606,22 @@ def hw(app: AppContext) -> None:
|
|
|
1606
1606
|
|
|
1607
1607
|
|
|
1608
1608
|
_BANDGAP_NOMINAL_MV = 1100
|
|
1609
|
+
# A plausibility bound for a 5 V supply pin, not a wire limit: the host sends
|
|
1610
|
+
# the bandgap, not the reading. A reading above it is a typo, refused before
|
|
1611
|
+
# any port opens.
|
|
1612
|
+
_CAL_VCC_MAX_V = 10.0
|
|
1609
1613
|
|
|
1610
1614
|
|
|
1611
1615
|
@cli.command(name="cal")
|
|
1612
1616
|
@click.option(
|
|
1613
1617
|
"--vcc",
|
|
1614
|
-
type=click.FloatRange(min=0, min_open=True),
|
|
1618
|
+
type=click.FloatRange(min=0, min_open=True, max=_CAL_VCC_MAX_V),
|
|
1615
1619
|
default=None,
|
|
1616
1620
|
metavar="VOLTS",
|
|
1617
|
-
help=
|
|
1621
|
+
help=(
|
|
1622
|
+
"Calibrate using a multimeter reading of the 5V pin, e.g. --vcc 5.09. "
|
|
1623
|
+
f"At most {_CAL_VCC_MAX_V:g} V."
|
|
1624
|
+
),
|
|
1618
1625
|
)
|
|
1619
1626
|
@click.option(
|
|
1620
1627
|
"--reset",
|
|
@@ -1654,21 +1661,43 @@ def cal(app: AppContext, vcc: float | None, reset: bool) -> None:
|
|
|
1654
1661
|
if vcc is not None:
|
|
1655
1662
|
measured_mv = int(round(vcc * 1000))
|
|
1656
1663
|
before = app.hardware_manager.read_calibration()
|
|
1657
|
-
if
|
|
1664
|
+
if before is None:
|
|
1665
|
+
click.echo(
|
|
1666
|
+
"Could not read the calibration. The firmware may be older than "
|
|
1667
|
+
"this CLI."
|
|
1668
|
+
)
|
|
1669
|
+
sys.exit(1)
|
|
1670
|
+
bandgap_adc = before["bandgap_adc"]
|
|
1671
|
+
if bandgap_adc == 0:
|
|
1672
|
+
click.echo("This board has no bandgap calibration (py32f071).")
|
|
1673
|
+
sys.exit(1)
|
|
1674
|
+
bandgap_mv = bandgap_from_measured_vcc(measured_mv, bandgap_adc)
|
|
1675
|
+
if bandgap_mv is None:
|
|
1658
1676
|
click.echo(
|
|
1659
1677
|
"Calibration refused. The result was outside 1000-1200 mV, which "
|
|
1660
1678
|
"means the reading does not match this board. Check that the "
|
|
1661
|
-
"multimeter is on the 5V pin and that the value is in volts.
|
|
1662
|
-
"A py32f071 board has no bandgap calibration and always "
|
|
1663
|
-
"refuses it."
|
|
1679
|
+
"multimeter is on the 5V pin and that the value is in volts."
|
|
1664
1680
|
)
|
|
1665
1681
|
sys.exit(1)
|
|
1682
|
+
if not app.hardware_manager.set_bandgap_mv(bandgap_mv):
|
|
1683
|
+
sys.exit(1)
|
|
1684
|
+
# Read the value back over a new connection, so the report is what the
|
|
1685
|
+
# board stored, not what this host sent.
|
|
1666
1686
|
after = app.hardware_manager.read_calibration()
|
|
1667
|
-
if
|
|
1687
|
+
if after is None:
|
|
1688
|
+
click.echo(
|
|
1689
|
+
f"Sent {bandgap_mv} mV, but could not read the calibration back. "
|
|
1690
|
+
"Run 'firestarter cal' to check it."
|
|
1691
|
+
)
|
|
1692
|
+
sys.exit(1)
|
|
1693
|
+
if after["bandgap_mv"] != bandgap_mv:
|
|
1668
1694
|
click.echo(
|
|
1669
|
-
f"
|
|
1695
|
+
f"Calibration not stored: the board reports {after['bandgap_mv']} "
|
|
1696
|
+
f"mV, expected {bandgap_mv} mV."
|
|
1670
1697
|
)
|
|
1671
|
-
|
|
1698
|
+
sys.exit(1)
|
|
1699
|
+
click.echo(f"Reference: {before['bandgap_mv']} mV -> {after['bandgap_mv']} mV")
|
|
1700
|
+
_echo_calibration_effect(after["bandgap_mv"])
|
|
1672
1701
|
sys.exit(0)
|
|
1673
1702
|
|
|
1674
1703
|
current = app.hardware_manager.read_calibration()
|
|
@@ -2285,16 +2314,22 @@ if _DEV_TOOLS_ENABLED:
|
|
|
2285
2314
|
@click.option(
|
|
2286
2315
|
"--read-settling",
|
|
2287
2316
|
"read_settling_us",
|
|
2288
|
-
type=
|
|
2317
|
+
type=click.IntRange(0, cf.READ_TIMING_MAX_US),
|
|
2289
2318
|
default=0,
|
|
2290
|
-
help=
|
|
2319
|
+
help=(
|
|
2320
|
+
"Address-settling delay before /CE assert "
|
|
2321
|
+
f"(µs, 0..{cf.READ_TIMING_MAX_US}; 0=firmware default 0µs)."
|
|
2322
|
+
),
|
|
2291
2323
|
)
|
|
2292
2324
|
@click.option(
|
|
2293
2325
|
"--read-strobe",
|
|
2294
2326
|
"read_strobe_us",
|
|
2295
|
-
type=
|
|
2327
|
+
type=click.IntRange(0, cf.READ_TIMING_MAX_US),
|
|
2296
2328
|
default=0,
|
|
2297
|
-
help=
|
|
2329
|
+
help=(
|
|
2330
|
+
"/CE read-strobe pulse width "
|
|
2331
|
+
f"(µs, 0..{cf.READ_TIMING_MAX_US}; 0=firmware default 3µs)."
|
|
2332
|
+
),
|
|
2298
2333
|
)
|
|
2299
2334
|
@click.pass_obj
|
|
2300
2335
|
@map_typed_errors
|
|
@@ -12,6 +12,7 @@ import logging
|
|
|
12
12
|
import struct
|
|
13
13
|
from typing import Any, List # noqa: UP035
|
|
14
14
|
|
|
15
|
+
from firestarter.command_frame import REFUSALS
|
|
15
16
|
from firestarter.constants import (
|
|
16
17
|
COMMAND_NAMES,
|
|
17
18
|
REVISION_0,
|
|
@@ -29,22 +30,58 @@ from firestarter.messages import (
|
|
|
29
30
|
DEBUG_CATALOG,
|
|
30
31
|
MSG_DATA_CHUNK,
|
|
31
32
|
MSG_DEBUG,
|
|
33
|
+
MSG_ERR_BAD_COMMAND,
|
|
34
|
+
MSG_ERR_VPP_HIGH_MV,
|
|
32
35
|
MSG_INFO_CMD,
|
|
33
36
|
MSG_INFO_HW,
|
|
34
37
|
MSG_INFO_PHYSICAL_HW,
|
|
38
|
+
MSG_INFO_REGISTERS,
|
|
35
39
|
MSG_OK_CFG,
|
|
36
40
|
MSG_OK_REV,
|
|
41
|
+
MSG_WARN_VPP_HIGH_MV,
|
|
42
|
+
MSG_WARN_VPP_LOW_MV,
|
|
37
43
|
SEVERITY_LABEL,
|
|
38
44
|
MessageDef,
|
|
39
45
|
)
|
|
40
46
|
|
|
41
47
|
logger = logging.getLogger("Codec")
|
|
42
48
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
# MSG_INFO_REGISTERS views (firmware sync: dev_tools.cpp DT_VIEW_*).
|
|
50
|
+
_REGISTER_VIEW_REG_FIRESTARTER = 1
|
|
51
|
+
_REGISTER_VIEW_ADDR = 2
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def volts(mv: int) -> str:
|
|
55
|
+
"""`'12.449 V'` for a millivolt value: every millivolt the firmware sent."""
|
|
56
|
+
return f"{mv / 1000:.3f} V"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _register_table(name: str, value: int, bits: int) -> list[str]:
|
|
60
|
+
"""One register as the three lines `dev reg` / `dev addr` print: the
|
|
61
|
+
value, the bit header and the bit row, D(bits-1) first."""
|
|
62
|
+
header = "|D8" if bits == 9 else ""
|
|
63
|
+
row = "".join(f"| {(value >> i) & 1}" for i in range(bits - 1, -1, -1))
|
|
64
|
+
return [
|
|
65
|
+
f"{name}: 0x{value:02X}",
|
|
66
|
+
f"{header}|D7|D6|D5|D4|D3|D2|D1|D0|",
|
|
67
|
+
f"{row}|",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _render_registers(msb: int, lsb: int, ctrl: int, remapped: int, view: int) -> str:
|
|
72
|
+
"""The bit tables for MSG_INFO_REGISTERS. The firmware sends raw values;
|
|
73
|
+
the table layout is the one the firmware used to print."""
|
|
74
|
+
lines = _register_table("MSB", msb, 8) + _register_table("LSB", lsb, 8)
|
|
75
|
+
if view == _REGISTER_VIEW_ADDR:
|
|
76
|
+
lines += _register_table("TOP addr", ctrl, 8)
|
|
77
|
+
lines += _register_table("CTRL remapped", remapped, 8)
|
|
78
|
+
elif view == _REGISTER_VIEW_REG_FIRESTARTER:
|
|
79
|
+
lines += _register_table("CTRL", ctrl, 9)
|
|
80
|
+
lines += _register_table("CTRL remapped", remapped, 8)
|
|
81
|
+
else:
|
|
82
|
+
lines += _register_table("CTRL", ctrl & 0xFF, 8)
|
|
83
|
+
return "\n".join(lines)
|
|
84
|
+
|
|
48
85
|
|
|
49
86
|
# REVISION_* byte → silkscreen-string mapping for MSG_OK_REV rendering.
|
|
50
87
|
# Mirrors firmware enum at firestarter/include/rurp_shield.h. Lookup-via-dict.get()
|
|
@@ -78,13 +115,10 @@ def format_message(msg_id: int, params: List[Any], entry: MessageDef) -> str | N
|
|
|
78
115
|
(the override is in effect; the second field is what the board detected)
|
|
79
116
|
|
|
80
117
|
P-03 MSG_OK_CFG — params[0]=r1 u32, params[1]=r2 u32, params[2]=override u8,
|
|
81
|
-
params[3]=bandgap_mv u16
|
|
82
|
-
override==0xFF → "R1: {r1}, R2: {r2}, Bandgap: {n} mV"
|
|
83
|
-
override!=0xFF → "R1: {r1}, R2: {r2}, Override HW: {silkscreen_str},
|
|
84
|
-
|
|
85
|
-
HardwareManager.read_calibration keys on it. The 9-byte form from older
|
|
86
|
-
firmware has no bandgap, so it renders without the Bandgap clause, and
|
|
87
|
-
read_calibration reports that firmware as too old.
|
|
118
|
+
params[3]=bandgap_mv u16, params[4]=bandgap_adc u16
|
|
119
|
+
override==0xFF → "R1: {r1}, R2: {r2}, Bandgap: {n} mV, Bandgap count: {c}"
|
|
120
|
+
override!=0xFF → "R1: {r1}, R2: {r2}, Override HW: {silkscreen_str},
|
|
121
|
+
Bandgap: {n} mV, Bandgap count: {c}"
|
|
88
122
|
Override clause now routes through
|
|
89
123
|
_REVISION_SILKSCREEN so the same byte that surfaces as "Rev 2.0-class"
|
|
90
124
|
via MSG_OK_REV no longer surfaces as "Rev2" on the adjacent ack line.
|
|
@@ -107,7 +141,7 @@ def format_message(msg_id: int, params: List[Any], entry: MessageDef) -> str | N
|
|
|
107
141
|
eff_str = _REVISION_SILKSCREEN.get(effective, f"Rev{effective}")
|
|
108
142
|
return f"{eff_str}, Detected HW: {phys_str}"
|
|
109
143
|
|
|
110
|
-
if msg_id == MSG_OK_CFG and len(params)
|
|
144
|
+
if msg_id == MSG_OK_CFG and len(params) == 5:
|
|
111
145
|
r1, r2, override = params[0], params[1], params[2]
|
|
112
146
|
head = f"R1: {r1}, R2: {r2}"
|
|
113
147
|
if override != 0xFF:
|
|
@@ -117,12 +151,7 @@ def format_message(msg_id: int, params: List[Any], entry: MessageDef) -> str | N
|
|
|
117
151
|
# No-space "Rev{n}" fallback mirrors the MSG_OK_REV branch shape.
|
|
118
152
|
override_str = _REVISION_SILKSCREEN.get(override, f"Rev{override}")
|
|
119
153
|
head += f", Override HW: {override_str}"
|
|
120
|
-
|
|
121
|
-
# Firmware before 3.1.0b4 sends no bandgap field.
|
|
122
|
-
return head
|
|
123
|
-
# v1.43 appended the calibrated reference. Rendered last so the
|
|
124
|
-
# override clause keeps the position every prior release put it in.
|
|
125
|
-
return f"{head}, Bandgap: {params[3]} mV"
|
|
154
|
+
return f"{head}, Bandgap: {params[3]} mV, Bandgap count: {params[4]}"
|
|
126
155
|
|
|
127
156
|
# Silkscreen-aware rendering for the two
|
|
128
157
|
# boot-time INFO surfaces that carry the same revision byte as
|
|
@@ -136,6 +165,22 @@ def format_message(msg_id: int, params: List[Any], entry: MessageDef) -> str | N
|
|
|
136
165
|
byte = params[0]
|
|
137
166
|
return f"Physical HW: {_REVISION_SILKSCREEN.get(byte, f'Rev{byte}')}"
|
|
138
167
|
|
|
168
|
+
if msg_id == MSG_ERR_BAD_COMMAND and len(params) == 1:
|
|
169
|
+
code = params[0]
|
|
170
|
+
return f"Command refused: {REFUSALS.get(code, f'reason {code}')}"
|
|
171
|
+
|
|
172
|
+
if msg_id == MSG_INFO_REGISTERS and len(params) == 5:
|
|
173
|
+
return _render_registers(*params)
|
|
174
|
+
|
|
175
|
+
# The firmware sends millivolts; show volts. The catalog text (mV) is the
|
|
176
|
+
# fallback for any other shape.
|
|
177
|
+
if msg_id in (MSG_WARN_VPP_LOW_MV, MSG_WARN_VPP_HIGH_MV, MSG_ERR_VPP_HIGH_MV):
|
|
178
|
+
if len(params) == 2:
|
|
179
|
+
measured, expected = params
|
|
180
|
+
relation = "<" if msg_id == MSG_WARN_VPP_LOW_MV else ">"
|
|
181
|
+
state = "low" if msg_id == MSG_WARN_VPP_LOW_MV else "high"
|
|
182
|
+
return f"VPP is {state}: {volts(measured)} {relation} {volts(expected)}"
|
|
183
|
+
|
|
139
184
|
if msg_id == MSG_INFO_CMD and len(params) == 1:
|
|
140
185
|
cmd = params[0]
|
|
141
186
|
name = COMMAND_NAMES.get(cmd)
|
|
@@ -244,27 +289,18 @@ def decode_id_frame(frame_len: int, body: bytes) -> LogMessage | None:
|
|
|
244
289
|
# entries carry param_bytes == -1 in the catalog; for those we
|
|
245
290
|
# cannot pre-validate, but _decode_param will surface any overrun
|
|
246
291
|
# via IndexError below.
|
|
247
|
-
|
|
248
|
-
is_legacy = legacy_len is not None and len(params_bytes) == legacy_len
|
|
249
|
-
if (
|
|
250
|
-
entry.param_bytes >= 0
|
|
251
|
-
and len(params_bytes) != entry.param_bytes
|
|
252
|
-
and not is_legacy
|
|
253
|
-
):
|
|
292
|
+
if entry.param_bytes >= 0 and len(params_bytes) != entry.param_bytes:
|
|
254
293
|
logger.warning(
|
|
255
294
|
f"Param shape mismatch for ID 0x{msg_id:02x} ({entry.name}): "
|
|
256
295
|
f"expected {entry.param_bytes} bytes, got {len(params_bytes)}"
|
|
257
296
|
)
|
|
258
297
|
return None
|
|
259
298
|
|
|
260
|
-
# Decode each param per the catalog grammar.
|
|
261
|
-
# at the end of its shorter param region.
|
|
299
|
+
# Decode each param per the catalog grammar.
|
|
262
300
|
values: List[Any] = [] # noqa: UP006
|
|
263
301
|
cursor = 0
|
|
264
302
|
try:
|
|
265
303
|
for ptype, _prender in entry.params:
|
|
266
|
-
if is_legacy and cursor >= len(params_bytes):
|
|
267
|
-
break
|
|
268
304
|
value, cursor = _decode_param(ptype, params_bytes, cursor)
|
|
269
305
|
values.append(value)
|
|
270
306
|
except (IndexError, struct.error, ValueError) as exc:
|
|
@@ -295,15 +331,19 @@ def decode_id_frame(frame_len: int, body: bytes) -> LogMessage | None:
|
|
|
295
331
|
|
|
296
332
|
# Extract raw-bytes payload for MSG_DATA_CHUNK (W-04) so the chip-read
|
|
297
333
|
# loop can obtain the chip data without a second read call.
|
|
298
|
-
|
|
334
|
+
payload = None
|
|
299
335
|
if (
|
|
300
336
|
msg_id == MSG_DATA_CHUNK
|
|
301
337
|
and values
|
|
302
338
|
and isinstance(values[0], (bytes, bytearray))
|
|
303
339
|
):
|
|
304
|
-
|
|
340
|
+
payload = bytes(values[0])
|
|
305
341
|
|
|
306
342
|
severity_label = SEVERITY_LABEL.get(entry.severity, f"SEV{entry.severity}")
|
|
307
343
|
return LogMessage(
|
|
308
|
-
severity=severity_label,
|
|
344
|
+
severity=severity_label,
|
|
345
|
+
text=text,
|
|
346
|
+
id=msg_id,
|
|
347
|
+
payload=payload,
|
|
348
|
+
params=tuple(values),
|
|
309
349
|
)
|