firestarter 3.2.4__tar.gz → 3.2.6__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.
Files changed (213) hide show
  1. {firestarter-3.2.4/firestarter.egg-info → firestarter-3.2.6}/PKG-INFO +1 -1
  2. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/_version.py +3 -3
  3. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/chip_resolver.py +10 -1
  4. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/cli_handlers.py +4 -2
  5. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/codec.py +3 -2
  6. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/data/chip_database.json +52 -16
  7. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/data/pinouts.json +10 -0
  8. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/database.py +5 -0
  9. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/firmware.py +20 -8
  10. firestarter-3.2.6/firestarter/flash4_erase_gate.py +104 -0
  11. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/ic_layout.py +7 -0
  12. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/jumper_table.py +10 -0
  13. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/serial_comm.py +10 -1
  14. {firestarter-3.2.4 → firestarter-3.2.6/firestarter.egg-info}/PKG-INFO +1 -1
  15. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter.egg-info/scm_file_list.json +1 -0
  16. firestarter-3.2.6/firestarter.egg-info/scm_version.json +8 -0
  17. {firestarter-3.2.4 → firestarter-3.2.6}/tests/__snapshots__/test_characterization.ambr +7 -4
  18. {firestarter-3.2.4 → firestarter-3.2.6}/tests/golden/chip_database_field_inventory.json +31 -27
  19. {firestarter-3.2.4 → firestarter-3.2.6}/tests/golden/wire_dict.json +35 -6
  20. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_build_db_inclusion.py +74 -3
  21. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_resolver.py +33 -3
  22. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_test.py +1 -1
  23. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_codec.py +2 -2
  24. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_codec_format_message.py +2 -2
  25. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_datasheet_overrides.py +1 -1
  26. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_decoder.py +3 -3
  27. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_eprom_database.py +16 -0
  28. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_extra_chips_supplement.py +22 -2
  29. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_firmware_install.py +62 -29
  30. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_flash4_erase_gate.py +98 -102
  31. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_info_vpp_and_erase.py +6 -5
  32. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_jumper_table.py +8 -5
  33. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_lock_status_class_partition.py +6 -5
  34. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_programming_vcc_census.py +25 -16
  35. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_vpp_rail_classification.py +21 -11
  36. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_blank_guard_pinning.py +4 -1
  37. firestarter-3.2.4/firestarter/flash4_erase_gate.py +0 -79
  38. firestarter-3.2.4/firestarter.egg-info/scm_version.json +0 -8
  39. {firestarter-3.2.4 → firestarter-3.2.6}/LICENSE +0 -0
  40. {firestarter-3.2.4 → firestarter-3.2.6}/MANIFEST.in +0 -0
  41. {firestarter-3.2.4 → firestarter-3.2.6}/README.md +0 -0
  42. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/__init__.py +0 -0
  43. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/address_parser.py +0 -0
  44. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/avr_tool.py +0 -0
  45. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/channel.py +0 -0
  46. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/chip_test.py +0 -0
  47. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/compare.py +0 -0
  48. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/config.py +0 -0
  49. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/constants.py +0 -0
  50. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/diagnostic_report.py +0 -0
  51. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/dip24_2816_erase_gate.py +0 -0
  52. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/eprom_info.py +0 -0
  53. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/eprom_operations.py +0 -0
  54. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/erase_support.py +0 -0
  55. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/exceptions.py +0 -0
  56. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/frame_parser.py +0 -0
  57. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/fw_release_gate.py +0 -0
  58. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/hardware.py +0 -0
  59. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/jp5_gate.py +0 -0
  60. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/lock_status.py +0 -0
  61. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/log_capture.py +0 -0
  62. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/logging_utils.py +0 -0
  63. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/main.py +0 -0
  64. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/messages.py +0 -0
  65. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/page_size_gate.py +0 -0
  66. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/protection_readability.py +0 -0
  67. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/py32_dfu.py +0 -0
  68. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/sdp_capability.py +0 -0
  69. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/sdp_honesty.py +0 -0
  70. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/submit.py +0 -0
  71. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/timestamps.py +0 -0
  72. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/transport_counters.py +0 -0
  73. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/utils.py +0 -0
  74. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/vpp_display.py +0 -0
  75. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter/write_blank_guard.py +0 -0
  76. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter.egg-info/SOURCES.txt +0 -0
  77. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter.egg-info/dependency_links.txt +0 -0
  78. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter.egg-info/entry_points.txt +0 -0
  79. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter.egg-info/requires.txt +0 -0
  80. {firestarter-3.2.4 → firestarter-3.2.6}/firestarter.egg-info/top_level.txt +0 -0
  81. {firestarter-3.2.4 → firestarter-3.2.6}/pyproject.toml +0 -0
  82. {firestarter-3.2.4 → firestarter-3.2.6}/setup.cfg +0 -0
  83. {firestarter-3.2.4 → firestarter-3.2.6}/tests/__init__.py +0 -0
  84. {firestarter-3.2.4 → firestarter-3.2.6}/tests/conftest.py +0 -0
  85. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fake_chip.py +0 -0
  86. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/part_number_delta.json +0 -0
  87. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/report_shapes.py +0 -0
  88. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/at28c256-full-all-ok-sdp.json +0 -0
  89. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/attr01-status-axis-transport-fault.json +0 -0
  90. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/gh20-at28c256-fail.json +0 -0
  91. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/gh23-w27e257-fail.json +0 -0
  92. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/gh28-m27c512-fail.json +0 -0
  93. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/gh47-sst27sf512-pass.json +0 -0
  94. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/m27c512-full-all-ok.json +0 -0
  95. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/m27c512-full-blank-check-bad.json +0 -0
  96. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/m27c512-full-canonical-name.json +0 -0
  97. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/m27c512-full-comma-joined-name.json +0 -0
  98. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/m27c512-full-runs-1.json +0 -0
  99. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/prune03-synthesized-fingerprint-match.json +0 -0
  100. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/sst27sf512-full-all-ok.json +0 -0
  101. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/sst27sf512-six-step-readback-gated.json +0 -0
  102. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/sst27sf512-six-step.json +0 -0
  103. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/synthetic-arm4-empty-results.json +0 -0
  104. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/synthetic-arm4-no-ok.json +0 -0
  105. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/uv-slot-write-pass.json +0 -0
  106. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/reports/w27e257-full-all-ok.json +0 -0
  107. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/shape_ids.json +0 -0
  108. {firestarter-3.2.4 → firestarter-3.2.6}/tests/fixtures/synthetic_nonzero_chip_id.py +0 -0
  109. {firestarter-3.2.4 → firestarter-3.2.6}/tests/golden/build_db_part_specific_constants.json +0 -0
  110. {firestarter-3.2.4 → firestarter-3.2.6}/tests/plan_corpus.py +0 -0
  111. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_address_parser.py +0 -0
  112. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_b15_page_size_corroboration.py +0 -0
  113. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_blast_radius_invariance.py +0 -0
  114. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_boot_block_hint.py +0 -0
  115. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_budget_failure_render.py +0 -0
  116. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_bug_characterization.py +0 -0
  117. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_build_db_constant_census.py +0 -0
  118. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_build_db_interpret_timing.py +0 -0
  119. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_build_db_pinout_fork.py +0 -0
  120. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_canonical_part_number.py +0 -0
  121. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_characterization.py +0 -0
  122. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_database_field_inventory.py +0 -0
  123. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_name_resolution.py +0 -0
  124. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_test_blank_check_order.py +0 -0
  125. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_test_cycle.py +0 -0
  126. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_test_sdp_leg.py +0 -0
  127. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_test_timing.py +0 -0
  128. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_chip_test_uv_slot_write.py +0 -0
  129. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_cli_handlers.py +0 -0
  130. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_click_group_gate_hook.py +0 -0
  131. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_cobs.py +0 -0
  132. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_compare.py +0 -0
  133. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_config.py +0 -0
  134. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_config_band_refusal.py +0 -0
  135. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_connect_cost_harness.py +0 -0
  136. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_consistency_check.py +0 -0
  137. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_coverage_floor_v18.py +0 -0
  138. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_database_conversion.py +0 -0
  139. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_derive_plan_no_drop_sweep.py +0 -0
  140. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_derive_plan_structural_sentinel.py +0 -0
  141. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_dev_group_channel_gating.py +0 -0
  142. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_dev_test_cmd.py +0 -0
  143. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_dev_tools_channel_gate.py +0 -0
  144. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_devtest_firmware_error_propagation.py +0 -0
  145. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_dfu_opcode_anchors.py +0 -0
  146. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_diagnostic_report.py +0 -0
  147. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_dip24_2816_erase_gate.py +0 -0
  148. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_endpoint_constants.py +0 -0
  149. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_eprom_info.py +0 -0
  150. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_eprom_operations.py +0 -0
  151. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_erase_blank_step_nonregression.py +0 -0
  152. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_erase_flag_invariants.py +0 -0
  153. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_error_code_seam.py +0 -0
  154. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_even_block.py +0 -0
  155. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fw_list_failure_vs_empty.py +0 -0
  156. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fw_port_targeting_and_blind_install.py +0 -0
  157. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fw_release_gate.py +0 -0
  158. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fw_update_dead_endpoint.py +0 -0
  159. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fw_update_path_gate.py +0 -0
  160. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fw_version_guard.py +0 -0
  161. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_fwguard.py +0 -0
  162. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_hardware.py +0 -0
  163. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_hw_revision_gate.py +0 -0
  164. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_ic_layout.py +0 -0
  165. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_jp5_gate.py +0 -0
  166. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_lock_status_cli.py +0 -0
  167. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_lock_status_wire.py +0 -0
  168. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_log_capture.py +0 -0
  169. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_logging_utils.py +0 -0
  170. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_matrix_artifact.py +0 -0
  171. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_page_size_alignment_refusal.py +0 -0
  172. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_page_size_invariants.py +0 -0
  173. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_page_size_write_refusal.py +0 -0
  174. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_parse_devtest_issue.py +0 -0
  175. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_probe_spurious_setup_ack.py +0 -0
  176. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_protect_flags_doc_measurements.py +0 -0
  177. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_protection_resolution.py +0 -0
  178. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_protection_status_catalog.py +0 -0
  179. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_protection_table_citations.py +0 -0
  180. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_protocol_not_implemented.py +0 -0
  181. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_protocol_not_implemented_production_path.py +0 -0
  182. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_provenance.py +0 -0
  183. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_pulse_us_override.py +0 -0
  184. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_py32_channel_gating.py +0 -0
  185. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_py32_dfu.py +0 -0
  186. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_py32_flash_map_host.py +0 -0
  187. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_py32_pyusb_absent.py +0 -0
  188. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_pyusb_api_surface.py +0 -0
  189. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_sdp_capability.py +0 -0
  190. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_sdp_db_invariant.py +0 -0
  191. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_sdp_honesty.py +0 -0
  192. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_serial_characterization.py +0 -0
  193. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_serial_comm.py +0 -0
  194. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_session_lease.py +0 -0
  195. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_setup_refusals.py +0 -0
  196. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_submit.py +0 -0
  197. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_timestamps.py +0 -0
  198. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_transport_counters.py +0 -0
  199. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_utils.py +0 -0
  200. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_uv_mask.py +0 -0
  201. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_validate_family_cmd.py +0 -0
  202. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_validate_oracle.py +0 -0
  203. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_validated_parts_regression_surface.py +0 -0
  204. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_vcc_margin_rail.py +0 -0
  205. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_vpp_decode_table.py +0 -0
  206. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_wire_dict_equivalence.py +0 -0
  207. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_blank_guard.py +0 -0
  208. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_guard_port_pinning.py +0 -0
  209. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_progress.py +0 -0
  210. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_response_budget.py +0 -0
  211. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_skip_erase_0x0d.py +0 -0
  212. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_skip_sdp_unlock.py +0 -0
  213. {firestarter-3.2.4 → firestarter-3.2.6}/tests/test_write_verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firestarter
3
- Version: 3.2.4
3
+ Version: 3.2.6
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.2.4'
22
- __version_tuple__ = version_tuple = (3, 2, 4)
21
+ __version__ = version = '3.2.6'
22
+ __version_tuple__ = version_tuple = (3, 2, 6)
23
23
 
24
- __commit_id__ = commit_id = 'g3023daa'
24
+ __commit_id__ = commit_id = 'g6a02a12'
@@ -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 EpromDatabase
12
+ from firestarter.database import ROM_CE, ROM_OE, EpromDatabase
13
13
  from firestarter.exceptions import (
14
14
  AmbiguousChipNameError,
15
15
  ChipNotFoundError,
@@ -133,6 +133,15 @@ def resolve_chip(name: str, db: EpromDatabase | None = None) -> dict[str, Any]:
133
133
  # A pin map with fewer address lines than the chip's size needs would
134
134
  # repeat one window of the chip across the whole operation, silently.
135
135
  bus = data.get("bus-config", {}).get("bus")
136
+ # The shield wires one socket pin to its CE driver and one to its OE
137
+ # driver. get_bus_config gives an address pin there the ROM_CE / ROM_OE
138
+ # sentinel, which is not a bus line the firmware can drive.
139
+ if bus and {ROM_CE, ROM_OE} & set(bus):
140
+ raise ChipNotImplementedError(
141
+ f"{name}: its pin map puts an address line on a socket pin that the "
142
+ "shield drives as CE or OE. The programmer cannot drive that "
143
+ "address line."
144
+ )
136
145
  if bus and data["memory-size"] > 1 << len(bus):
137
146
  raise ChipNotImplementedError(
138
147
  f"{name}: its pin map has {len(bus)} address lines, which reach "
@@ -1443,9 +1443,11 @@ def erase(
1443
1443
 
1444
1444
  eprom_data = resolve_chip(eprom, db=app.db)
1445
1445
 
1446
+ # The database leaves FLAG_CAN_ERASE clear for flash4, so `write` never
1447
+ # carries it. The erase sets it for this command only; setup_command
1448
+ # refuses it on firmware without the 5 V software chip erase.
1446
1449
  if flash4_erase_gate.is_flash4(eprom_data):
1447
- click.echo(flash4_erase_gate.refusal_text(eprom))
1448
- sys.exit(0 if ignore_unsupported else 1)
1450
+ eprom_data = flash4_erase_gate.with_erase_flag(eprom_data)
1449
1451
 
1450
1452
  full_record = app.db.get_eprom(eprom) or {}
1451
1453
  if dip24_2816_erase_gate.is_affected(full_record.get("pin-map")):
@@ -74,7 +74,8 @@ def format_message(msg_id: int, params: List[Any], entry: MessageDef) -> str | N
74
74
 
75
75
  P-02 MSG_OK_REV — params[0]=physical u8, params[1]=effective u8
76
76
  effective==0xFF → "Rev{physical}" (no override)
77
- effective!=0xFF → "Rev{effective}, Override HW: Rev{physical}"
77
+ effective!=0xFF → "Rev{effective}, Detected HW: Rev{physical}"
78
+ (the override is in effect; the second field is what the board detected)
78
79
 
79
80
  P-03 MSG_OK_CFG — params[0]=r1 u32, params[1]=r2 u32, params[2]=override u8,
80
81
  params[3]=bandgap_mv u16 (absent on firmware before 3.1.0b4)
@@ -104,7 +105,7 @@ def format_message(msg_id: int, params: List[Any], entry: MessageDef) -> str | N
104
105
  if effective == 0xFF:
105
106
  return phys_str
106
107
  eff_str = _REVISION_SILKSCREEN.get(effective, f"Rev{effective}")
107
- return f"{eff_str}, Override HW: {phys_str}"
108
+ return f"{eff_str}, Detected HW: {phys_str}"
108
109
 
109
110
  if msg_id == MSG_OK_CFG and len(params) in (3, 4):
110
111
  r1, r2, override = params[0], params[1], params[2]
@@ -1710,7 +1710,8 @@
1710
1710
  "protect_on_after": false,
1711
1711
  "pulse_duration_us": 100
1712
1712
  },
1713
- "support_status": "supported"
1713
+ "support_status": "protocol-not-implemented",
1714
+ "unsupported_reason": "protocol not implemented: pin map DIP28_27011 gives 14 address lines, which reach 16384 of the chip's 131072 bytes. The programmer implements no way to reach the rest."
1714
1715
  },
1715
1716
  {
1716
1717
  "electrical": {
@@ -5989,7 +5990,7 @@
5989
5990
  "vpp_mv": 12500
5990
5991
  },
5991
5992
  "part_number": "MBM27C1000P,MBM27C1000",
5992
- "pinout": "DIP32_27C020",
5993
+ "pinout": "DIP32_27C301",
5993
5994
  "programming": {
5994
5995
  "algorithm": 8,
5995
5996
  "chip_id_check": true,
@@ -5999,7 +6000,8 @@
5999
6000
  "protect_on_after": false,
6000
6001
  "pulse_duration_us": 500
6001
6002
  },
6002
- "support_status": "supported"
6003
+ "support_status": "adapter-required",
6004
+ "unsupported_reason": "adapter required: the shield drives chip pin 22 as CE and pin 24 as OE, but pin map DIP32_27C301 needs other functions on pins [2, 24]. An adapter must move those pins."
6003
6005
  },
6004
6006
  {
6005
6007
  "electrical": {
@@ -6058,7 +6060,7 @@
6058
6060
  "vpp_mv": 12500
6059
6061
  },
6060
6062
  "part_number": "MBM27C2000P,MBM27C2000",
6061
- "pinout": "DIP32_27C020",
6063
+ "pinout": "DIP32_27C301",
6062
6064
  "programming": {
6063
6065
  "algorithm": 8,
6064
6066
  "chip_id_check": true,
@@ -6068,7 +6070,8 @@
6068
6070
  "protect_on_after": false,
6069
6071
  "pulse_duration_us": 100
6070
6072
  },
6071
- "support_status": "supported"
6073
+ "support_status": "adapter-required",
6074
+ "unsupported_reason": "adapter required: the shield drives chip pin 22 as CE and pin 24 as OE, but pin map DIP32_27C301 needs other functions on pins [2, 24]. An adapter must move those pins."
6072
6075
  },
6073
6076
  {
6074
6077
  "electrical": {
@@ -6464,7 +6467,7 @@
6464
6467
  "vpp_mv": 12500
6465
6468
  },
6466
6469
  "part_number": "HN27C301AG,HN27C301AP,HN27C301AFP",
6467
- "pinout": "DIP32_27C020",
6470
+ "pinout": "DIP32_27C301",
6468
6471
  "programming": {
6469
6472
  "algorithm": 8,
6470
6473
  "chip_id_check": true,
@@ -6474,7 +6477,8 @@
6474
6477
  "protect_on_after": false,
6475
6478
  "pulse_duration_us": 100
6476
6479
  },
6477
- "support_status": "supported"
6480
+ "support_status": "adapter-required",
6481
+ "unsupported_reason": "adapter required: the shield drives chip pin 22 as CE and pin 24 as OE, but pin map DIP32_27C301 needs other functions on pins [2, 24]. An adapter must move those pins."
6478
6482
  },
6479
6483
  {
6480
6484
  "electrical": {
@@ -6486,7 +6490,7 @@
6486
6490
  "vpp_mv": 12500
6487
6491
  },
6488
6492
  "part_number": "HN27C301G",
6489
- "pinout": "DIP32_27C020",
6493
+ "pinout": "DIP32_27C301",
6490
6494
  "programming": {
6491
6495
  "algorithm": 8,
6492
6496
  "chip_id_check": false,
@@ -6496,7 +6500,8 @@
6496
6500
  "protect_on_after": false,
6497
6501
  "pulse_duration_us": 100
6498
6502
  },
6499
- "support_status": "supported"
6503
+ "support_status": "adapter-required",
6504
+ "unsupported_reason": "adapter required: the shield drives chip pin 22 as CE and pin 24 as OE, but pin map DIP32_27C301 needs other functions on pins [2, 24]. An adapter must move those pins."
6500
6505
  },
6501
6506
  {
6502
6507
  "electrical": {
@@ -7407,7 +7412,8 @@
7407
7412
  "protect_on_after": false,
7408
7413
  "pulse_duration_us": 100
7409
7414
  },
7410
- "support_status": "supported"
7415
+ "support_status": "protocol-not-implemented",
7416
+ "unsupported_reason": "protocol not implemented: pin map DIP28_27011 gives 14 address lines, which reach 16384 of the chip's 131072 bytes. The programmer implements no way to reach the rest."
7411
7417
  },
7412
7418
  {
7413
7419
  "electrical": {
@@ -7429,7 +7435,8 @@
7429
7435
  "protect_on_after": false,
7430
7436
  "pulse_duration_us": 100
7431
7437
  },
7432
- "support_status": "supported"
7438
+ "support_status": "protocol-not-implemented",
7439
+ "unsupported_reason": "protocol not implemented: pin map DIP28_27011 gives 14 address lines, which reach 16384 of the chip's 131072 bytes. The programmer implements no way to reach the rest."
7433
7440
  },
7434
7441
  {
7435
7442
  "electrical": {
@@ -11966,7 +11973,7 @@
11966
11973
  "vpp_mv": 13000
11967
11974
  },
11968
11975
  "part_number": "M27C1000",
11969
- "pinout": "DIP32_27C020",
11976
+ "pinout": "DIP32_27C301",
11970
11977
  "programming": {
11971
11978
  "algorithm": 8,
11972
11979
  "chip_id_check": true,
@@ -11976,7 +11983,8 @@
11976
11983
  "protect_on_after": false,
11977
11984
  "pulse_duration_us": 100
11978
11985
  },
11979
- "support_status": "supported"
11986
+ "support_status": "adapter-required",
11987
+ "unsupported_reason": "adapter required: the shield drives chip pin 22 as CE and pin 24 as OE, but pin map DIP32_27C301 needs other functions on pins [2, 24]. An adapter must move those pins."
11980
11988
  },
11981
11989
  {
11982
11990
  "electrical": {
@@ -15655,8 +15663,9 @@
15655
15663
  },
15656
15664
  "provenance": "VAR-05 / D-10 \u2014 TI 2532 is a physically real 4KB 24-pin UV-EPROM with a NON-JEDEC pinout (2532 != JEDEC 2732); absent from minipro infoic.xml. The 2516_EPROM.pdf is the TI 2500-series UV-EPROM family datasheet covering the 2516/2532 lineage; VPP=pin 21 / A11=pin 18 / !PD-PGM=pin 20 distinguishes the 2532 from the DIP24_2732 layout. Modeled by the new DIP24_2532 pinout entry. Carries the same UNVERIFIED posture as the 2516 row (no on-hand bench proof).",
15657
15665
  "source": "non-upstream-supplement",
15658
- "support_status": "supported",
15659
- "verification_note": "SAFE-04 / D-11: resolvable for read/info but NOT write-graduated \u2014 no on-hand 2532 silicon was bench-tested. Non-JEDEC 24-pin oddball; the DIP24_2532 pinout is a tentative leaf layout requiring bench validation. VPP lands on pin 21 (DIP24_2532 vpp-pin) per the TI family datasheet \u2014 the GATE-03 structural VPP guard is satisfied.",
15666
+ "support_status": "adapter-required",
15667
+ "unsupported_reason": "adapter required: the shield drives chip pin 18 as CE and pin 20 as OE, but on the 2532 pin 18 is A11 and pin 20 is PD/PGM. The shield cannot drive A11, so only the lower 2 KB can be addressed, and the program pulse would go to A11. An adapter must move those pins.",
15668
+ "verification_note": "SAFE-04 / D-11: no on-hand 2532 silicon was bench-tested. Non-JEDEC 24-pin oddball; the DIP24_2532 pinout is a tentative leaf layout requiring bench validation. VPP lands on pin 21 (DIP24_2532 vpp-pin) per the TI family datasheet \u2014 the GATE-03 structural VPP guard is satisfied. Socket pin 18 is the shield's CE driver, not an address latch, so host 3.2.4 sent bus line 256 (ROM_CE) for A11; firmware 3.2.1 truncated it to line 0, and later firmware refuses the frame. resolve_chip now refuses the row on its status.",
15660
15669
  "verification_status": "UNVERIFIED"
15661
15670
  }
15662
15671
  ],
@@ -16441,6 +16450,33 @@
16441
16450
  "pulse_duration_us": 100
16442
16451
  },
16443
16452
  "support_status": "supported"
16453
+ },
16454
+ {
16455
+ "datasheet": "datasheets/TMM27128AD.pdf",
16456
+ "electrical": {
16457
+ "pin_count": 28,
16458
+ "size_bytes": 16384,
16459
+ "type": "UV-EPROM",
16460
+ "vcc_mv": 5000,
16461
+ "vdd_mv": 6000,
16462
+ "vpp_mv": 12500
16463
+ },
16464
+ "part_number": "TMM27128AD,TMM27128AD-15,TMM27128AD-150,TMM27128AD-20,TMM27128AD-200",
16465
+ "pinout": "DIP28_27128",
16466
+ "programming": {
16467
+ "algorithm": 7,
16468
+ "chip_id_check": true,
16469
+ "chip_id_value": "0x000098d3",
16470
+ "infoic_page_size_raw": 0,
16471
+ "protect_off_before": false,
16472
+ "protect_on_after": false,
16473
+ "pulse_duration_us": 1000
16474
+ },
16475
+ "provenance": "Physically real 28-pin NMOS UV-EPROM (Toshiba TMM27128AD, 16384 x 8), absent from minipro infoic.xml, whose TOSHIBA/KIOXIA section has no 27128-class part. Datasheet attached to henols/firestarter#73 by the reporter, vendored as datasheets/TMM27128AD.pdf (Toshiba pages G-27 to G-36). Page G-27: pin 1 VPP, pin 20 CE, pin 22 OE, pin 26 A13, pin 27 PGM, pin compatible with i27128A (DIP28_27128). Page G-30 HIGH SPEED PROGRAM OPERATION: VCC 5.75/6.0/6.25 V, VPP 12.0/12.5/13.0 V, tPW 0.95/1.0/1.05 ms. Page G-33/G-34: 1 ms PGM pulses verified after each, up to 25 times, then an over-program pulse of 3 times the pulses used. Page G-35 ELECTRIC SIGNATURE MODE: manufacturer code 98, device code D3. Read VCC 5 V (page G-28).",
16476
+ "source": "non-upstream-supplement",
16477
+ "support_status": "supported",
16478
+ "verification_note": "Resolvable for read/info. No working sample has been tested: the chip reported in henols/firestarter#73 was dead. The datasheet program VCC is 6.0 V, but the shield holds VCC at 5 V, and the firmware does not send the over-program pulse.",
16479
+ "verification_status": "UNVERIFIED"
16444
16480
  }
16445
16481
  ],
16446
16482
  "WED": [
@@ -16669,7 +16705,7 @@
16669
16705
  "type": "EEPROM",
16670
16706
  "vcc_mv": 5000,
16671
16707
  "vdd_mv": 5000,
16672
- "vpp_mv": 13500
16708
+ "vpp_mv": 12000
16673
16709
  },
16674
16710
  "part_number": "W27E257",
16675
16711
  "pinout": "DIP28_27256",
@@ -115,6 +115,16 @@
115
115
  "ce-pin": [22], "oe-pin": [24], "pgm-pin": [31]
116
116
  }
117
117
  },
118
+ "DIP32_27C301": {
119
+ "name": "Mask-ROM-compatible 32-pin UV-EPROM (27C301 / 27C1000 — OE on pin 2, A16 on pin 24)",
120
+ "comment": "infoic.xml pin map 12 with protocol 0x08 and variant_lo 0x00. Only MBM27C1000, MBM27C2000, HN27C301G, HN27C301AG and M27C1000 carry it; variant_lo 0x01 in the same map is the JEDEC 27C020 layout. Pin functions are from datasheets/MBM27C1000.pdf (page 4-61 pin assignment, page 4-63 pin description): pin 1 VPP, pin 2 OE, pin 22 CE, pin 24 A16, pin 31 PGM, pin 30 NC. one-rom chip-types.json type 27C301 (aliases 27C1000, 27C100) gives the same OE and A16 pins. JEDEC 27C010/27C020 swap pins 2 and 24, so on DIP32_27C020 these parts drive OE high whenever A16 is 1 (henols/firestarter#70). Pin 30 is A17 for the 256K MBM27C2000 row: <maps> index 12 bonds it, and it is the only bonded pin that has no other function on this layout. No MBM27C2000 datasheet is vendored to confirm it.",
121
+ "pins": {
122
+ "vcc-pin": [32], "gnd-pin": [16], "vpp-pin": [1],
123
+ "address-bus-pins": [12, 11, 10, 9, 8, 7, 6, 5, 27, 26, 23, 25, 4, 28, 29, 3, 24, 30],
124
+ "data-bus-pins": [13, 14, 15, 17, 18, 19, 20, 21],
125
+ "ce-pin": [22], "oe-pin": [2], "pgm-pin": [31]
126
+ }
127
+ },
118
128
  "DIP32_27C801": {
119
129
  "name": "JEDEC 32-pin UV-EPROM 1M (27C080 / M27C801 — A19 on pin 1, VPP on pin 24)",
120
130
  "comment": "Per piersfinlayson/one-rom docs/CHIP-TYPES.md (datasheet-verified): the 8 Mbit 27C080/M27C801 class has 20 address lines (A0-A19) with A19 at pin 1, and VPP at pin 24 shared with /OE — the same OE/VPP sharing DIP28_27512 uses at pin 22. /PGM is pin 22 shared with /CE. Distinct from DIP32_STD (27C040 class: pin 1 = VPP, pin 31 = A18, 19 address lines) and from DIP32_27C020 (pin 31 = PGM). Assigned by resolve_pinout_key to pin_count=32 && pm_idx=12 && proto_id=0x08 && variant_lo=0x03 — the eight 1 MB rows AM27C080, AM27LV080, AT27C080, M27C801 (x2), MX27C8000, MX27C8000A, UPD27C8001. HARDWARE: A19 at pin 1 means socket pin 1 carries an address line, and on Rev 2.x shields socket pin 1 is also the VPP-enable line (JP5 = A19_CUT bridges it to the switched VPP node). JP5 must be cut for these parts, per the shield silkscreen.",
@@ -165,6 +165,11 @@ _MOVED_PIN_MAPS = {
165
165
  "chips. 27128/27C128 chips use DIP28_27128 and 27011 chips use "
166
166
  "DIP28_27011.",
167
167
  ),
168
+ "DIP32_27C020": (
169
+ ("DIP32_27C301",),
170
+ "Your pin-map override for DIP32_27C020 no longer applies to "
171
+ "27C301/27C1000 chips (OE on pin 2, A16 on pin 24). They use DIP32_27C301.",
172
+ ),
168
173
  "DIP32_28C512_EEPROM": (
169
174
  ("DIP32_28C040",),
170
175
  "Your pin-map override for DIP32_28C512_EEPROM is not used. "
@@ -335,8 +335,8 @@ class FirmwareManager:
335
335
  """Router: returns (resolved_version, download_url) or (None, None) on failure.
336
336
 
337
337
  channel='stable' → delegates to fetch_latest_release_info (back-compat shim).
338
- channel='pre' → paginates /releases, filters prerelease=True, sorts by PEP 440
339
- descending, takes highest; falls back to stable if none.
338
+ channel='pre' → paginates /releases, takes the highest non-draft release by
339
+ PEP 440, stable or pre-release; falls back to stable if none.
340
340
  channel='pinned' → fetches /releases/tags/{version} directly.
341
341
  """ # noqa: E501
342
342
  if channel == "stable":
@@ -374,10 +374,12 @@ class FirmwareManager:
374
374
  logger.error(f"Failed to fetch releases for pre-release channel: {e}")
375
375
  return None, None
376
376
 
377
- # Filter: prerelease=True AND draft=False, skip unparseable tags.
377
+ # Every non-draft release, stable or pre-release, as `pip --pre`
378
+ # does: a stable 3.2.2 must win over an older 3.1.1b2. Skip
379
+ # unparseable tags.
378
380
  candidates = []
379
381
  for r in all_releases:
380
- if not r.get("prerelease") or r.get("draft"):
382
+ if r.get("draft"):
381
383
  continue
382
384
  tag = r.get("tag_name", "")
383
385
  try:
@@ -387,7 +389,7 @@ class FirmwareManager:
387
389
 
388
390
  if not candidates:
389
391
  logger.info(
390
- "No pre-release firmware available — falling back to stable "
392
+ "No firmware release with a parseable tag — falling back to stable "
391
393
  "(matches pip --pre semantics)."
392
394
  )
393
395
  return self.fetch_latest_release_info(board=board)
@@ -517,8 +519,13 @@ class FirmwareManager:
517
519
  avrdude_path_override: str | None,
518
520
  avrdude_config_override: str | None,
519
521
  target_port: str | None,
522
+ force: bool = False,
520
523
  ) -> bool:
521
- """Internal method to perform the Avrdude flashing process."""
524
+ """Internal method to perform the Avrdude flashing process.
525
+
526
+ `force` adds avrdude's `-F`, which skips the device-signature check,
527
+ for example for a 328PB clone bootloader that reports a 328P signature.
528
+ """
522
529
  start_time = time.time()
523
530
 
524
531
  partno, programmer_id, baud_rate = (
@@ -594,7 +601,9 @@ class FirmwareManager:
594
601
  avrdude_config_path=avrdude_config_path,
595
602
  )
596
603
 
597
- stderr_output, return_code = avrdude.flash_firmware(hex_file_path)
604
+ stderr_output, return_code = avrdude.flash_firmware(
605
+ hex_file_path, extra_flags=["-F"] if force else None
606
+ )
598
607
  if return_code == 0:
599
608
  logger.info(
600
609
  f"Firmware successfully updated on {port_to_flash} ({time.time() - start_time:.2f}s)" # noqa: E501
@@ -741,6 +750,7 @@ class FirmwareManager:
741
750
  avrdude_config_override: str | None,
742
751
  target_port: str | None,
743
752
  usb_id: str | None = None,
753
+ force: bool = False,
744
754
  ) -> bool:
745
755
  """Dispatch to the flasher for this board.
746
756
 
@@ -755,6 +765,7 @@ class FirmwareManager:
755
765
  avrdude_path_override=avrdude_path_override,
756
766
  avrdude_config_override=avrdude_config_override,
757
767
  target_port=target_port,
768
+ force=force,
758
769
  )
759
770
 
760
771
  def manage_firmware_update(
@@ -778,7 +789,7 @@ class FirmwareManager:
778
789
  Returns True if an operation (check or install) was successful in some sense, False on major failure.
779
790
 
780
791
  channel='stable' → uses /releases/latest (default, INST-01 non-regression).
781
- channel='pre' → selects highest pre-release.
792
+ channel='pre' → selects the highest release, stable or pre-release.
782
793
  channel='pinned' → uses exact tag from pinned_version.
783
794
  """ # noqa: E501
784
795
  connected_port, current_version, current_board = self.check_current_firmware(
@@ -969,6 +980,7 @@ class FirmwareManager:
969
980
  avrdude_config_override=avrdude_config_override,
970
981
  target_port=port_to_use,
971
982
  usb_id=usb_id,
983
+ force=bool(force_install),
972
984
  )
973
985
  # Clean up downloaded file
974
986
  if os.path.exists(hex_file):
@@ -0,0 +1,104 @@
1
+ """
2
+ Project Name: Firestarter
3
+ Copyright (c) 2024 Henrik Olsson
4
+
5
+ Permission is hereby granted under MIT license.
6
+
7
+ Flash4 (protocol 0x05) erase policy.
8
+
9
+ Firmware 3.2.2 added a software chip erase for protocol 0x05 (the six-cycle
10
+ AA/55/80/AA/55/10 sequence, 5 V only). Older firmware is not safe to send
11
+ that erase to:
12
+
13
+ - 3.0.0b8 to 3.0.0b26 erase a 0x05 part with the VPP regulator and VPE
14
+ switched on: 12 V on a 5 V part.
15
+ - 3.0.0b27 to 3.2.1 answer "not supported".
16
+
17
+ So `FLAG_CAN_ERASE` stays clear for algorithm 5 in the database
18
+ (`erase_support.erase_accepted`), and `write` never carries it. Only the
19
+ `erase` command sets it, for that one command
20
+ (`with_erase_flag`), and `SerialCommunicator.setup_command` refuses the
21
+ command before the operation starts when the firmware is older than
22
+ `ERASE_MIN_FIRMWARE` (`needs_newer_firmware`).
23
+
24
+ Like `jp5_gate.py` and `sdp_capability.py`, this is a pure predicate module:
25
+ no I/O, no environment reads, no serial access.
26
+
27
+ `is_flash4` FAILS OPEN on absent evidence (a wire dict with no `algorithm`
28
+ key is "not flash4"), unlike `jp5_gate` and `sdp_capability`. Refusing every
29
+ chip this predicate cannot classify would break `erase` for chips that are
30
+ not flash4 at all.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import re
36
+ from typing import Any, Mapping # noqa: UP035
37
+
38
+ from firestarter.constants import COMMAND_ERASE, FLAG_CAN_ERASE
39
+
40
+ FLASH4_PROTOCOL_ID = 5
41
+
42
+ # The first firmware with the software chip erase for protocol 0x05.
43
+ ERASE_MIN_FIRMWARE = (3, 2, 2)
44
+
45
+ _FIRMWARE_REFUSAL_FORMAT = (
46
+ "Erasing this chip needs firmware {minimum} or later; this board runs "
47
+ "{version}. Update it with 'firestarter fw --install'."
48
+ )
49
+
50
+
51
+ def is_flash4(programmer_data: Mapping[str, Any] | None) -> bool:
52
+ """True when the wire dict's `algorithm` value is the flash4 protocol id.
53
+
54
+ The comparison is against `algorithm`, not `flags & FLAG_CAN_ERASE`: the
55
+ flag is also clear for UV-EPROM and SRAM parts, so a flag-based
56
+ predicate would widen this past its flash4-only scope.
57
+
58
+ Returns False for a missing, `None`, or empty `programmer_data`, and for
59
+ a dict with no `algorithm` key.
60
+ """
61
+ if not programmer_data:
62
+ return False
63
+ return programmer_data.get("algorithm") == FLASH4_PROTOCOL_ID
64
+
65
+
66
+ def with_erase_flag(programmer_data: Mapping[str, Any]) -> dict[str, Any]:
67
+ """A copy of `programmer_data` with `FLAG_CAN_ERASE` set, for `erase` only."""
68
+ data = dict(programmer_data)
69
+ data["flags"] = int(data.get("flags") or 0) | FLAG_CAN_ERASE
70
+ return data
71
+
72
+
73
+ def _version_tuple(version: str | None) -> tuple[int, int, int] | None:
74
+ match = re.match(r"(\d+)\.(\d+)\.(\d+)", version or "")
75
+ if not match:
76
+ return None
77
+ return (int(match.group(1)), int(match.group(2)), int(match.group(3)))
78
+
79
+
80
+ def needs_newer_firmware(
81
+ command: Mapping[str, Any], firmware_version: str | None
82
+ ) -> bool:
83
+ """True when `command` is a flash4 erase that `firmware_version` cannot run safely.
84
+
85
+ Only an erase command that carries `FLAG_CAN_ERASE` for a flash4 part is
86
+ affected; every other command returns False. A version that cannot be
87
+ parsed counts as too old, because the 3.0.0 pre-releases with the 12 V
88
+ erase are older than every version this check accepts. A pre-release
89
+ or development build of 3.2.2 (`3.2.2rc1`, `3.2.2.dev5`) counts as 3.2.2.
90
+ """
91
+ if command.get("cmd") != COMMAND_ERASE or not is_flash4(command):
92
+ return False
93
+ if not int(command.get("flags") or 0) & FLAG_CAN_ERASE:
94
+ return False
95
+ parsed = _version_tuple(firmware_version)
96
+ return parsed is None or parsed < ERASE_MIN_FIRMWARE
97
+
98
+
99
+ def firmware_refusal_text(firmware_version: str | None) -> str:
100
+ """The one-line refusal for a flash4 erase on firmware that is too old."""
101
+ return _FIRMWARE_REFUSAL_FORMAT.format(
102
+ minimum=".".join(str(n) for n in ERASE_MIN_FIRMWARE),
103
+ version=firmware_version or "an unknown version",
104
+ )
@@ -12,6 +12,7 @@ from typing import Dict, List # noqa: UP035
12
12
  from firestarter import jumper_table
13
13
  from firestarter.database import EpromDatabase, format_mv
14
14
  from firestarter.erase_support import erase_accepted, is_electrically_erasable
15
+ from firestarter.flash4_erase_gate import FLASH4_PROTOCOL_ID
15
16
  from firestarter.vpp_display import shows_programming_vpp
16
17
 
17
18
  logger = logging.getLogger("EpromSpecBuilder")
@@ -19,6 +20,7 @@ logger = logging.getLogger("EpromSpecBuilder")
19
20
  CAN_ERASE_YES = "yes (electrically erasable)"
20
21
  CAN_ERASE_UV_ONLY = "no (UV erase only)"
21
22
  CAN_ERASE_NOT_SUPPORTED = "no (erase is not supported for this chip)"
23
+ CAN_ERASE_FLASH4 = "yes (needs firmware 3.2.2 or later)"
22
24
 
23
25
 
24
26
  class EpromSpecBuilder:
@@ -524,6 +526,11 @@ class EpromSpecBuilder:
524
526
  etype, eprom_data.get("protocol-id"), eprom_data.get("pin-map")
525
527
  ):
526
528
  output_data["can_erase_str"] = CAN_ERASE_YES
529
+ elif is_electrically_erasable(etype) and (
530
+ eprom_data.get("protocol-id") == FLASH4_PROTOCOL_ID
531
+ ):
532
+ # `erase` sets FLAG_CAN_ERASE for flash4 itself (flash4_erase_gate).
533
+ output_data["can_erase_str"] = CAN_ERASE_FLASH4
527
534
  elif is_electrically_erasable(etype):
528
535
  output_data["can_erase_str"] = CAN_ERASE_NOT_SUPPORTED
529
536
  elif etype == "UV-EPROM":
@@ -401,6 +401,16 @@ JUMPER_TABLE: Mapping[str, JumperEntry] = {
401
401
  rev20_jp4=Jp4Rev20.OPEN,
402
402
  rev22_jp4=Jp4Rev22.NO_JUMPER,
403
403
  ),
404
+ # Pins 1, 28, 30 and 32 have the DIP32_27C020 functions; only pins 2 and 24 differ.
405
+ "DIP32_27C301": JumperEntry(
406
+ vpp_lands=VppLanding.SOCKET_1,
407
+ pin1_signal="VPP",
408
+ jp1=Jp1.A13,
409
+ jp2=Jp2.A17,
410
+ jp3=Jp3.PIN32,
411
+ rev20_jp4=Jp4Rev20.OPEN,
412
+ rev22_jp4=Jp4Rev22.NO_JUMPER,
413
+ ),
404
414
  # VPP shares OE. Chip pin 1 is A19. The JP5 gate (jp5_gate.py) controls write and erase.
405
415
  "DIP32_27C801": JumperEntry(
406
416
  vpp_lands=VppLanding.OE,
@@ -20,6 +20,7 @@ import serial.serialutil
20
20
  import serial.tools.list_ports
21
21
 
22
22
  import firestarter.codec as codec
23
+ import firestarter.flash4_erase_gate as flash4_erase_gate
23
24
  import firestarter.transport_counters as transport_counters
24
25
  from firestarter.config import ConfigManager # Assuming ConfigManager is refactored
25
26
  from firestarter.constants import (
@@ -957,9 +958,17 @@ class SerialCommunicator:
957
958
  # to major=3 later. Set FIRESTARTER_DEV_ALLOW_PRE_V12=1 to bypass when # noqa: E501
958
959
  # bench-testing a current host against a historical (v2.x) firmware build. # noqa: E501
959
960
  SerialCommunicator._validate_firmware_version(
960
- version_match.group(0),
961
+ version_match.group(0).rstrip("."),
961
962
  allow_pre_v12=os.environ.get("FIRESTARTER_DEV_ALLOW_PRE_V12") == "1",
962
963
  )
964
+ # A flash4 erase on firmware older than 3.2.2 reaches either a
965
+ # 12 V erase (3.0.0b8-b26) or a "not supported" answer.
966
+ if flash4_erase_gate.needs_newer_firmware(command_to_send, identity):
967
+ raise FirmwareOutdatedError(
968
+ flash4_erase_gate.firmware_refusal_text(
969
+ identity.split(":")[0] if identity else None
970
+ )
971
+ )
963
972
 
964
973
  # Shield-revision gate — ordered after the version check because
965
974
  # firmware old enough to fail that check cannot be trusted to have
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: firestarter
3
- Version: 3.2.4
3
+ Version: 3.2.6
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
@@ -26,6 +26,7 @@
26
26
  "datasheets/MBM27C4001.pdf",
27
27
  "datasheets/MX27C4000.pdf",
28
28
  "datasheets/SST39SF0x0A.pdf",
29
+ "datasheets/TMM27128AD.pdf",
29
30
  "datasheets/TMS2764.pdf",
30
31
  "datasheets/UPD27128.pdf",
31
32
  "datasheets/W27C020.pdf",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "3.2.6",
3
+ "distance": 0,
4
+ "node": "g6a02a12",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-27"
8
+ }
@@ -595,6 +595,8 @@
595
595
  Eprom Info
596
596
  Name: MBM27C1000P,MBM27C1000
597
597
  Manufacturer: FUJITSU
598
+ Support status: adapter-required
599
+ Reason: adapter required: the shield drives chip pin 22 as CE and pin 24 as OE, but pin map DIP32_27C301 needs other functions on pins [2, 24]. An adapter must move those pins.
598
600
  Number of pins: 32
599
601
  Memory size 0x20000
600
602
  Type: UV-EPROM
@@ -610,14 +612,14 @@
610
612
  32-DIP package
611
613
  -----v-----
612
614
  VPP -| 1 32 |- VCC
613
- A16 -| 2 31 |- PGM
615
+ OE -| 2 31 |- PGM
614
616
  A15 -| 3 30 |- A17
615
617
  A12 -| 4 29 |- A14
616
618
  A7 -| 5 28 |- A13
617
619
  A6 -| 6 27 |- A8
618
620
  A5 -| 7 26 |- A9
619
621
  A4 -| 8 25 |- A11
620
- A3 -| 9 24 |- OE
622
+ A3 -| 9 24 |- A16
621
623
  A2 -| 10 23 |- A10
622
624
  A1 -| 11 22 |- CE
623
625
  A0 -| 12 21 |- D7
@@ -1441,6 +1443,7 @@
1441
1443
  | TC54256AF,TC54256AP | TOSHIBA/KIOXIA | 28 | 0x98C4 | UV-EPROM | 12.0v|
1442
1444
  | TC57256D | TOSHIBA/KIOXIA | 28 | 0x9845 | UV-EPROM | 13.0v|
1443
1445
  | TC57512AD | TOSHIBA/KIOXIA | 28 | 0x9885 | UV-EPROM | 13.0v|
1446
+ | TMM27128AD,TMM27128…| TOSHIBA/KIOXIA | 28 | 0x98D3 | UV-EPROM | 12.5v|
1444
1447
  | WE128K8 | WED | 32 | | Flash/EEPROM| - |
1445
1448
  | WE256K8 | WED | 32 | | Flash/EEPROM| - |
1446
1449
  | WE512K8 | WED | 32 | | Flash/EEPROM| - |
@@ -1450,7 +1453,7 @@
1450
1453
  | W27C04,W27C040,W27E…| WINBOND | 32 | 0xDA86 | EEPROM | 12.0v|
1451
1454
  | W27C257 | WINBOND | 28 | 0xDA02 | EEPROM | 12.0v|
1452
1455
  | W27C512,W27E512 | WINBOND | 28 | 0xDA08 | EEPROM | 12.0v|
1453
- | W27E257 | WINBOND | 28 | 0xDA02 | EEPROM | 13.5v|
1456
+ | W27E257 | WINBOND | 28 | 0xDA02 | EEPROM | 12.0v|
1454
1457
  | W29C010,W29C011,W29…| WINBOND | 32 | 0xDAC1 | Flash/EEPROM| - |
1455
1458
  | W29C020,W29C020C,W2…| WINBOND | 32 | 0xDA45 | Flash/EEPROM| - |
1456
1459
  | W29C040,W29C042 | WINBOND | 32 | 0xDA46 | Flash/EEPROM| - |
@@ -1579,7 +1582,7 @@
1579
1582
  | W27C04,W27C040,W27E…| WINBOND | 32 | 0xDA86 | EEPROM | 12.0v|
1580
1583
  | W27C257 | WINBOND | 28 | 0xDA02 | EEPROM | 12.0v|
1581
1584
  | W27C512,W27E512 | WINBOND | 28 | 0xDA08 | EEPROM | 12.0v|
1582
- | W27E257 | WINBOND | 28 | 0xDA02 | EEPROM | 13.5v|
1585
+ | W27E257 | WINBOND | 28 | 0xDA02 | EEPROM | 12.0v|
1583
1586
  +---------------------+------------------+------+------------+-------------+------+
1584
1587
 
1585
1588
  '''