feat: boot mode parsing, FSBL-first flow, UI reorder
1. Boot mode parsing:
- BootInfo.boot_mode field added
- Matches 'Boot mode is JTAG', 'Boot Mode: QSPI', etc.
- Fixed false version match: 'Boot mode is' no longer
captures 'mode' as a version string
2. UART window cleanup:
- Parsed boot info (mode/IP/ver) now only goes to main log
- Raw serial lines still appear in UART window
3. Step 3 FSBL enforcement:
- BIT step: after fpga -file, CPU is immediately halted (stop)
to prevent Flash code from enabling MMU
- ELF step: always dow FSBL → con → stop → dow bootloader
4. UI reorder:
- FSBL ELF selector moved BEFORE Bootloader BIT
- New layout: FSBL → BIT → ELF → Bootloader BIN → Flash → FW BIN
- Fixed grid overlap (FSBL and Flash were both at row 6)
This commit is contained in:
@@ -214,7 +214,7 @@ def _program_with_xsct(
|
||||
if callback:
|
||||
callback("progress", "Initializing PS then programming FPGA...")
|
||||
|
||||
# Build TCL: PS init first, then FPGA config, then post-config
|
||||
# Build TCL: PS init → FPGA config → post-config → halt CPU → disable Flash boot
|
||||
lines = ["connect"]
|
||||
if ps7_init_tcl and Path(ps7_init_tcl).exists():
|
||||
lines.append("targets 1")
|
||||
@@ -227,6 +227,11 @@ def _program_with_xsct(
|
||||
lines.append("ps7_post_config")
|
||||
if callback:
|
||||
callback("progress", "PS post-config (PL-PS interfaces)")
|
||||
# After FPGA config, CPU may be reset — halt it to prevent Flash code
|
||||
# from enabling MMU before the ELF step can download FSBL
|
||||
lines.append("targets 2")
|
||||
lines.append("after 100")
|
||||
lines.append("stop")
|
||||
# Prevent boot from Flash by disabling PCAP reconfiguration
|
||||
lines.append("mwr 0xF800025C 0x00000001")
|
||||
lines.append("exit")
|
||||
|
||||
+19
-18
@@ -433,7 +433,7 @@ class MainWindow(ctk.CTk):
|
||||
row=0, column=0, sticky="nsew",
|
||||
padx=PADDING, pady=PADDING,
|
||||
)
|
||||
panel.grid_rowconfigure(8, weight=1)
|
||||
panel.grid_rowconfigure(9, weight=1)
|
||||
|
||||
title = ctk.CTkLabel(
|
||||
panel,
|
||||
@@ -466,6 +466,17 @@ class MainWindow(ctk.CTk):
|
||||
self._ip_entry = ctk.CTkEntry(ip_frame, textvariable=self._ip_string_var)
|
||||
self._ip_entry.grid(row=0, column=1, sticky="nsew", padx=PADDING_SMALL)
|
||||
|
||||
# FSBL ELF path (First Stage Bootloader — initialises PS, forces JTAG boot)
|
||||
self._fsbl_selector = FileSelector(
|
||||
panel,
|
||||
label_text="FSBL ELF (.elf):",
|
||||
file_types=[("ELF files", "*.elf"), ("All files", "*")],
|
||||
)
|
||||
if self._config.fsbl_elf_path:
|
||||
resolved = self._config.resolve_path(self._config.fsbl_elf_path)
|
||||
self._fsbl_selector.set_path(str(resolved))
|
||||
self._fsbl_selector.grid(row=3, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
|
||||
# BIT path (Bootloader)
|
||||
self._bit_selector = FileSelector(
|
||||
panel,
|
||||
@@ -475,7 +486,7 @@ class MainWindow(ctk.CTk):
|
||||
if self._config.bootloader_bit_path:
|
||||
resolved = self._config.resolve_path(self._config.bootloader_bit_path)
|
||||
self._bit_selector.set_path(str(resolved))
|
||||
self._bit_selector.grid(row=3, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
self._bit_selector.grid(row=4, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
|
||||
# ELF path (Bootloader)
|
||||
self._elf_selector = FileSelector(
|
||||
@@ -486,7 +497,7 @@ class MainWindow(ctk.CTk):
|
||||
if self._config.bootloader_elf_path:
|
||||
resolved = self._config.resolve_path(self._config.bootloader_elf_path)
|
||||
self._elf_selector.set_path(str(resolved))
|
||||
self._elf_selector.grid(row=4, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
self._elf_selector.grid(row=5, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
|
||||
# Bootloader BIN path (Flash programming)
|
||||
self._bootloader_bin_selector = FileSelector(
|
||||
@@ -497,22 +508,11 @@ class MainWindow(ctk.CTk):
|
||||
if self._config.bootloader_bin_path:
|
||||
resolved = self._config.resolve_path(self._config.bootloader_bin_path)
|
||||
self._bootloader_bin_selector.set_path(str(resolved))
|
||||
self._bootloader_bin_selector.grid(row=5, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
|
||||
# FSBL ELF path (required by program_flash for QSPI programming)
|
||||
self._fsbl_selector = FileSelector(
|
||||
panel,
|
||||
label_text="FSBL ELF (.elf):",
|
||||
file_types=[("ELF files", "*.elf"), ("All files", "*")],
|
||||
)
|
||||
if self._config.fsbl_elf_path:
|
||||
resolved = self._config.resolve_path(self._config.fsbl_elf_path)
|
||||
self._fsbl_selector.set_path(str(resolved))
|
||||
self._fsbl_selector.grid(row=6, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
self._bootloader_bin_selector.grid(row=6, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
|
||||
# Flash options
|
||||
flash_frame = ctk.CTkFrame(panel)
|
||||
flash_frame.grid(row=6, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
flash_frame.grid(row=7, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
flash_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self._erase_cb_var = ctk.BooleanVar(value=self._config.erase_all)
|
||||
@@ -1164,8 +1164,9 @@ class MainWindow(ctk.CTk):
|
||||
self._uart_monitor = UartMonitor(port, self._config.serial_baudrate)
|
||||
self._uart_monitor.on_line = lambda line: self._log_message(f" [UART] {line}")
|
||||
self._uart_monitor.on_boot_info = lambda info: (
|
||||
self._log_message(f" [UART] IP={info.ip_address}, Ver={info.version}")
|
||||
if info.ip_address or info.version else None
|
||||
self._log_message(
|
||||
f" [UART] Boot={info.boot_mode}, IP={info.ip_address}, Ver={info.version}"
|
||||
) if any([info.boot_mode, info.ip_address, info.version]) else None
|
||||
)
|
||||
self._uart_monitor.on_error = lambda e: self._log_message(f" [UART] Error: {e}")
|
||||
if self._uart_monitor.start():
|
||||
|
||||
+4
-3
@@ -995,15 +995,16 @@ class UartMonitorWindow(ctk.CTkToplevel):
|
||||
|
||||
def _on_boot_info(self, info) -> None:
|
||||
parts = []
|
||||
if info.boot_mode:
|
||||
parts.append(f"Boot={info.boot_mode}")
|
||||
if info.ip_address:
|
||||
parts.append(f"IP={info.ip_address}")
|
||||
if info.version:
|
||||
parts.append(f"Ver={info.version}")
|
||||
if parts:
|
||||
msg = f"[UART] {' '.join(parts)}"
|
||||
self.after(0, lambda: self._log(f"{msg}\n"))
|
||||
# Forward to main window log
|
||||
# Forward to main log only (not UART window)
|
||||
if self._on_log:
|
||||
msg = f"[UART] {' '.join(parts)}"
|
||||
self.after(0, lambda: self._on_log(msg))
|
||||
|
||||
def _on_error(self, error: str) -> None:
|
||||
|
||||
+14
-1
@@ -23,6 +23,7 @@ class BootInfo:
|
||||
ip_address: str = ""
|
||||
version: str = ""
|
||||
boot_message: str = ""
|
||||
boot_mode: str = ""
|
||||
raw_lines: list[str] | None = None
|
||||
|
||||
|
||||
@@ -73,7 +74,7 @@ def parse_boot_output(lines: list[str]) -> BootInfo:
|
||||
ip_pattern = re.compile(r"\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b")
|
||||
version_patterns = [
|
||||
re.compile(r"[Vv]ersion[:\s]+([^\s;,\n]+)", re.IGNORECASE),
|
||||
re.compile(r"[Bb]oot\s+([^\s;,\n]+)", re.IGNORECASE),
|
||||
re.compile(r"[Bb]oot\s+(?:version\s+)?([Vv]?\d+\.\d+[^\s;,\n]*)", re.IGNORECASE),
|
||||
re.compile(r"[Ff]irm[Ww]are:?\s*([^\s;,\n]+)", re.IGNORECASE),
|
||||
re.compile(r"(?:^|[\s:=])([Vv]\d+\.\d+\.\d+[^\s;,\n]*)", re.IGNORECASE),
|
||||
]
|
||||
@@ -84,6 +85,10 @@ def parse_boot_output(lines: list[str]) -> BootInfo:
|
||||
re.compile(r"Linux\s+booted", re.IGNORECASE),
|
||||
re.compile(r"QSYS\s+ready", re.IGNORECASE),
|
||||
]
|
||||
boot_mode_patterns = [
|
||||
re.compile(r"[Bb]oot\s*[Mm]ode\s*(?:is)?\s*[:\[=\s]*(\w+)"),
|
||||
re.compile(r"BootMode\s*[:\[=\s]*(\w+)", re.IGNORECASE),
|
||||
]
|
||||
|
||||
for line in lines:
|
||||
# Extract IP address
|
||||
@@ -107,6 +112,14 @@ def parse_boot_output(lines: list[str]) -> BootInfo:
|
||||
info.boot_message = line.strip()
|
||||
break
|
||||
|
||||
# Extract boot mode
|
||||
if not info.boot_mode:
|
||||
for pattern in boot_mode_patterns:
|
||||
match = pattern.search(line)
|
||||
if match:
|
||||
info.boot_mode = match.group(1)
|
||||
break
|
||||
|
||||
return info
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user