#!/bin/sh
# select_de25_profile.sh — pick the DE25-Nano board build profile WITHOUT hand-editing
# the QSF. Rewrites the two managed marker blocks in the board QSF:
#     # >>> DE25_PROFILE_FIXTURE >>> ... # <<< DE25_PROFILE_FIXTURE <<<
#     # >>> DE25_PROFILE_SCENE   >>> ... # <<< DE25_PROFILE_SCENE   <<<
# Everything else in the QSF (EMIF macros GS_LPDDR4B_FB/GS_LPDDR_FB, pin/IP assignments)
# is profile-independent and untouched. After switching, re-fit in Quartus as usual.
#
# Usage:
#   ./scripts/select_de25_profile.sh lpddr_tex          # Ch322 LPDDR-backed texture demo (tritex)
#   ./scripts/select_de25_profile.sh lpddr128_scanout   # Ch321 128x128 LPDDR framebuffer scanout
#   ./scripts/select_de25_profile.sh --list             # list profiles + show the active one
#
# Add a profile by extending the case blocks below (fixture mems + scene VERILOG_MACROs).

set -u
QSF="${QSF:-synth/de25_nano/top_psmct32_raster_demo/de25_nano_psmct32_raster_demo_top.qsf}"
PROFILE="${1:-}"
MEMDIR="sim/data/top_psmct32_raster_demo"

[ -f "$QSF" ] || { echo "QSF not found: $QSF (run from repo root, or set QSF=)"; exit 2; }

mac() { printf 'set_global_assignment -name VERILOG_MACRO "%s"\n' "$1"; }
fix() { # $1=bios stem $2=payload stem
    mac "TOP_PSMCT32_RASTER_DEMO_BIOS_IMAGE_FILE=\\\"$MEMDIR/$1.mem\\\""
    mac "TOP_PSMCT32_RASTER_DEMO_PAYLOAD_IMAGE_FILE=\\\"$MEMDIR/$2.mem\\\""
}

case "$PROFILE" in
  lpddr_tex)
      FIXTURE="$(fix bios_tritex payload_tritex)"
      SCENE="$(mac GS_LPDDR_TEX_DEMO=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  lpddr128_scanout)
      FIXTURE="$(fix bios_tile_lpddr128 payload_tile_lpddr128)"
      SCENE="$(mac GS_TILE_LPDDR128_DEMO=1)"
      ;;
  tile_spill)
      # Ch324 — 4x4 MULTI-TILE COLOR+Z spill/reload two-batch depth proof (64x64 raster
      # FB in LPDDR). GS_TILE_SPILL_DEMO selects the bram-top render config (PSMCT32,
      # close_combined, TILE_LOCAL, TILE_COLS/ROWS=4); GS_TILE_SPILL enables the de25
      # LPDDR spill writers + gs_tile_reload (per-tile reload base) + TILE_SPILL_ENABLE.
      # Ch325 — scaled to 8x8 / 128x128 (bios_tile_spill8x8). Ch324 4x4 fixture retired here.
      # Ch327b — scaled to 16x16 / 256x256 (bios_tile_spill16x16), line-buffer scanout.
      FIXTURE="$(fix bios_tile_spill16x16 payload_tile_spill16x16)"
      SCENE="$(mac GS_TILE_SPILL_DEMO=1)
$(mac GS_TILE_SPILL=1)"
      ;;
  feeder)
      # Ch330 — runtime command-list feeder. The setup-only bootlet uploads the texture
      # (bios/payload_feeder_setup), the feeder draws list A, then the HPS streams list B
      # into the staging RAM over the bridge (0x0D8/0x0DC/0x0E4) and retriggers (0x0E8) —
      # the image changes with no RBF rebuild / reset. GS_FEEDER_DEMO selects the bram-top
      # config (FEEDER_ENABLE=1, HEARTBEAT_SPLICE_ENABLE=0, 4x4 TILE_PRIM_COUNT=4).
      FIXTURE="$(fix bios_feeder_setup payload_feeder_setup)"
      # FEEDER_STG_INIT_FILE bitstream-inits the staging RAM with list A so the first
      # auto-run draws a clean scene at power-up (sim never defines this; TBs $readmemh directly).
      SCENE="$(mac GS_FEEDER_DEMO=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_stg_cap4.mem\\\"")"
      ;;
  feeder_persp)
      # Ch342 — PERSPECTIVE-correct textured triangles through the feeder on the S1 path (tiling/
      # combined OFF, PERSPECTIVE_CORRECT=1). The setup-only bootlet uploads a 16x16 checkerboard
      # (bios/payload_persp_feeder_setup); FEEDER_STG_INIT renders the perspective checkerboard floor
      # at boot (feeder_persp.mem, word0[32]=1 -> RGBAQ/ST/XYZ2). The HPS can then stream an authentic
      # cube_persp scene over the bridge (ps2_feeder, ABE=0). Combined-TAZ perspective is a follow-on.
      FIXTURE="$(fix bios_persp_feeder_setup payload_persp_feeder_setup)"
      SCENE="$(mac GS_FEEDER_PERSP_DEMO=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_persp.mem\\\"")"
      ;;
  feeder_cube)
      # Ch343 — the AUTHENTIC cube on silicon. Same GS_FEEDER_PERSP_DEMO bram-top config as
      # feeder_persp (ABE=0 S1 perspective, .RAM_SIZE_BYTES=32 KiB), but the fixtures are the real
      # cubes-dump content: the boot bootlet uploads the extracted 64x64 cube texture @ TBP=64
      # (bios/payload_cube_setup, QWC=1030) and FEEDER_STG_INIT renders the 27 translated perspective
      # triangles (feeder_cube_persp.mem). All three .mem are dump-derived/LOCAL (gitignored) — generate
      # with `python3 tools/gs_make_cube_fixture.py`. Closes the GS dump -> extraction -> ST/Q -> pixels arc.
      FIXTURE="$(fix bios_cube_setup payload_cube_setup)"
      SCENE="$(mac GS_FEEDER_PERSP_DEMO=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_cube_persp.mem\\\"")"
      ;;
  texalpha)
      # Ch344 — TEXTURED + source-over alpha SPRITE on silicon (boot payload, no feeder). SPRITE_TEX_ALPHA=1.
      # bios/payload_texalpha upload an 8x8 alpha-checker texture + blue BG sprite + a textured-alpha overlay
      # sprite. On HDMI: a gray/blue checkerboard (opaque texels gray, transparent texels reveal the BG).
      FIXTURE="$(fix bios_texalpha payload_texalpha)"
      SCENE="$(mac GS_TEXALPHA_DEMO=1)"
      ;;
  feeder_sprite)
      # Ch345a — RUNTIME FEEDER textured-alpha SPRITEs (sprite_mode staging). SPRITE_TEX_ALPHA=1 + FEEDER_ENABLE=1.
      # bios/payload_sprite_setup uploads an 8x8 alpha texture + blue BG; FEEDER_STG_INIT=feeder_sprite streams
      # 3 textured-alpha sprites over it. On HDMI: 3 gray/blue alpha-checker squares on blue (runtime ingestion).
      FIXTURE="$(fix bios_sprite_setup payload_sprite_setup)"
      SCENE="$(mac GS_FEEDER_SPRITE_DEMO=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_sprite.mem\\\"")"
      ;;
  sh3_clut)
      # Ch347 — AUTHENTIC Silent Hill 3 64x64-crop PSMT8 texture + real CLUT, DECAL on silicon. The bootlet
      # (bios/payload_sh3_clut) BITBLT-uploads the 256-entry CLUT + 64x64 PSMT8 indices, fires the VRAM->CLUT
      # load (TEX0 CLD=1, CSM2-linear), draws a 64x64 DECAL sprite. HEARTBEAT_SPLICE off (set in the profile).
      # Fixtures are SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_extract_sh3_clut.py` then
      # `python3 tools/gs_make_sh3_fixture.py` with the local SH3 dump BEFORE fitting.
      FIXTURE="$(fix bios_sh3_clut payload_sh3_clut)"
      SCENE="$(mac GS_SH3_CLUT_DEMO=1)"
      ;;
  sh3_persp)
      # Ch348 — AUTHENTIC SH3 PSMT8 texture + real CLUT through the PERSPECTIVE-triangle FEEDER path. The
      # setup bootlet (bios/payload_sh3_persp) uploads the 64x64 PSMT8 texture + 256-entry CLUT; the feeder
      # staging (feeder_sh3_persp) draws a perspective quad with TEX0(PSMT8,CLD=1) -> the feeder commit fires
      # the VRAM->CLUT load. Fixtures are SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_extract_sh3_clut.py`
      # then `python3 tools/gs_make_sh3_persp_fixture.py` with the local SH3 dump BEFORE fitting.
      FIXTURE="$(fix bios_sh3_persp payload_sh3_persp)"
      SCENE="$(mac GS_SH3_PERSP_DEMO=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_sh3_persp.mem\\\"")"
      ;;
  sh3_real_draw)
      # Ch350/351/352 — the ACTUAL Ch349 SH3 draw CROP on silicon (Codex board gate). Real draw geometry
      # (idx89761 lamppost/bench, host-clipped to a 256x120 viewport) + the reconstructed 512x512 PSMT8 texture
      # in LPDDR (GS_LPDDR_TEX cache @ 0x200000) + the real CSM1 CLUT (bootlet 256x1 BITBLT to CBP, grid bytes
      # verbatim) + Ch351 widened perspective precision (PERSP_RECIP_IDX_BITS=11). 128 KiB crop VRAM (under the
      # read2 tripwire). Golden oracle: tb_top_psmct32_sh3_real_draw_demo (ALL 96.2% <=1 texel, clut_bad=0).
      # BOARD ORDER: (1) fit this profile, (2) HPS write-probe uploads sh3_real_tex_lpddr.mem to LPDDR @0x200000
      # + checksum-verify via the read-probe, (3) feeder auto-runs the bitstream-init scene, (4) verify HDMI vs
      # the crop reference (captures/.../recon/sh3_real_ref.png). Fixtures SH3-derived -> LOCAL/gitignored:
      # run `python3 tools/gs_make_sh3_real_draw_fixture.py` FIRST.
      FIXTURE="$(fix bios_sh3_real payload_sh3_real)"
      SCENE="$(mac GS_SH3_REAL_DRAW_DEMO=1)
$(mac GS_LPDDR_TEX=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_sh3_real.mem\\\"")"
      ;;
  sh3_lpddr_fb)
      # Ch353 — the SH3 real draw on an LPDDR-ONLY direct PSMCT32 framebuffer (the FULL 256x334 bounding box, NOT
      # the Ch352 256x120 BRAM crop). Same feeder-driven SH3 config as sh3_real_draw (LPDDR texture cache @0x200000
      # + real CSM1 CLUT + Ch351 widened perspective), but the bram-top runs with FB_LPDDR_ONLY (the PSMCT32 writer
      # commits the frame to LPDDR @ base 0 instead of the BRAM mirror) and FEEDER_AUTOSTART=0 (no boot render). The
      # line-buffer scanout serves the LPDDR FB, HARD-gated on the ordered frame_drained ack (no timeout). VRAM stays
      # 128 KiB / CBP=480 (Codex option A). The static QSF EMIF/GS_LPDDR4B_FB/USE_QSYS_TOP already provide the EMIF +
      # writer + line-buffer scanout. Proven end-to-end by tb_top_psmct32_sh3_lpddr_fb_seq (the mandatory fit gate).
      # BOARD ORDER (Codex host sequence): (1) wait setup/CLUT + feeder ready (0x0D8[0]); (2) HPS PRECLEAR the LPDDR
      # FB 0x000000..0x0537FF via the write-probe; (3) upload the 512x512 PSMT8 texture to LPDDR @0x200000; (4) fill +
      # checksum-verify the texture cache (0x054/0x070=fbdeaa32); (5) set FB base 0 (0x01C=0), canary off, ARM the
      # writer (0x018[0]); (6) pulse feeder GO (0x0E8); (7) confirm drain (poll LPDDR_STATUS 0x02C[0] idle + LPDDR_BYTES
      # 0x030 != 0); (8) scanout auto-enables from frame_drained — raise video_src (0x018[2]=1); scanout_lb is forced.
      # Fixtures SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_make_sh3_real_draw_fixture.py --full-frame` FIRST.
      FIXTURE="$(fix bios_sh3_full payload_sh3_full)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_LPDDR_TEX=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_sh3_full.mem\\\"")"
      ;;
  sh3_lpddr_fb_multi)
      # Ch354 Brick 1 — MULTI-DRAW: 3 authentic SH3 draws (idx89548/89761/89974) that SHARE one texture/CLUT,
      # composited into ONE LPDDR PSMCT32 framebuffer (256x338, the union bbox). Same LPDDR-FB path as sh3_lpddr_fb
      # (FEEDER_AUTOSTART=0 + render-epoch EOF + frame_drained hard gate + line-buffer scanout) but GS_SH3_LPDDR_FB_MULTI
      # raises FEEDER_STG_WORDS 768->2048 (204-tri combined list) and SCANOUT_LB_NROWS 334->338. Accumulation is
      # bit-exact (combined RTL == RTL-composed singles, 0/86528 px); per-draw <=1-texel fidelity is draw-dependent
      # (97.5/95.6/93.2%, the existing perspective-sampling limit, NOT an acceptance gate). Proven by
      # tb_top_psmct32_sh3_multidraw. GS_SH3_LPDDR_FB + GS_LPDDR4B_FB + GS_LPDDR_TEX all apply (the profile sets them).
      # BOARD ORDER: identical to sh3_lpddr_fb, but preclear the LARGER FB range:
      #   sudo ./ps2_sh3_tex_upload sh3_real_tex_lpddr.mem --lpddr-fb --fb-rows 338   (preclears 0..0x54800).
      # Fixtures SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_make_sh3_multidraw_fixture.py <dump>
      # --draw-list 89548,89761,89974 --tag multi --emit` FIRST.
      FIXTURE="$(fix bios_sh3_multi payload_sh3_multi)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_MULTI=1)
$(mac GS_LPDDR_TEX=1)
$(mac "FEEDER_STG_INIT_FILE=\\\"$MEMDIR/feeder_sh3_multi.mem\\\"")"
      ;;
  sh3_lpddr_fb_multitex)
      # Ch355 Brick 1 — MULTI-TEXTURE: 2 SH3 draws with DIFFERENT textures/CLUTs (A=idx19562 tbp=11264/CBP_A,
      # B=idx89761 tbp=9216/CBP_B) composited into ONE 320x381 LPDDR FB (FBW=5, stride 1280, 0x77100, 15240 beats/frame)
      # via SCENE-LEVEL texture rebind + RUNTIME staged-list retriggering. GS_SH3_LPDDR_FB_MULTITEX raises
      # FEEDER_STG_WORDS->2048 + SCANOUT_LB to 320x381. The bootlet (bios/payload_sh3_mt) uploads BOTH relocated CLUTs
      # (CBP_A=480, CBP_B=484); the HOST streams each list at runtime (NO FEEDER_STG_INIT_FILE — feeder starts empty).
      # Accumulation bit-exact + Q-canonicalized (feeder canonicalizes homogeneous coords to positive Q; NOT native
      # signed-Q). Proven by tb_top_psmct32_sh3_multitex.
      # BOARD RUN (runtime two-group flow, host streams the lists + rebinds the cache):
      #   sudo ./ps2_sh3_multitex sh3_mtA_tex_lpddr.mem sh3_mtB_tex_lpddr.mem feeder_sh3_mtA.mem feeder_sh3_mtB.mem
      # Fixtures SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_make_sh3_multitex_fixture.py <dump> --emit` FIRST.
      FIXTURE="$(fix bios_sh3_mt payload_sh3_mt)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_MULTITEX=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  sh3_lpddr_fb_sched)
      # Ch356 — N-TEXTURE SCHEDULER: N (default 3) authentic SH3 draws with DIFFERENT textures/CLUTs
      # (E0=idx11671 tbp=10240/CBP480, E1=idx19562 tbp=11264/CBP484, E2=idx89761 tbp=9216/CBP488) composited into ONE
      # 384x381 LPDDR FB (FBW=6, stride 1536, 0x8ee00, 18288 beats/frame) via a DATA-DRIVEN scheduler over epoch
      # descriptors (scene-level texture rebind + runtime staged-list retriggering). GS_SH3_LPDDR_FB_SCHED raises
      # FEEDER_STG_WORDS->2048 + SCANOUT_LB to 384x381. The bootlet (bios/payload_sh3_sched) preloads ALL N relocated
      # CLUTs; the HOST streams each list at runtime (NO FEEDER_STG_INIT_FILE). Accumulation proven 100% BIT-FOR-BIT
      # (compose_sched.py) + Q-canonicalized; tb_top_psmct32_sh3_sched (+ _compose gate).
      # BOARD RUN (data-driven; host reads the descriptor table + streams each list, rebinds the cache per epoch):
      #   sudo ./ps2_sh3_sched sh3_sched_epochs.txt        (all fixtures in the cwd)
      # Fixtures SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_make_sh3_scheduler_fixture.py <dump> --emit` FIRST.
      FIXTURE="$(fix bios_sh3_sched payload_sh3_sched)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_SCHED=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  sh3_lpddr_fb_640)
      # Ch357 — NATIVE 640x480 LPDDR framebuffer: the Ch356 N-texture scheduler on a full-frame 640x480 FB
      # (FBW=10, stride 2560, size 0x12C000, 80 beats/row, 38400 beats/frame). Draws land at their AUTHENTIC screen
      # coordinates (NO union-origin translation). Scheduler/host UNCHANGED from Ch356; only the FB geometry differs.
      # GS_SH3_LPDDR_FB_640 sets SCANOUT_LB 640x480 + FEEDER_STG_WORDS=2048. Bootlet (bios/payload_sh3_s640) preloads
      # all N relocated CLUTs; the HOST streams each list at runtime (NO FEEDER_STG_INIT_FILE). FB[0..0x12C000) disjoint
      # from texture region [0x200000..). Composition proven 100% BIT-FOR-BIT (compose_sched.py); tb_top_psmct32_sh3_sched640.
      # BOARD RUN (same data-driven host, different descriptor table):
      #   sudo ./ps2_sh3_sched sh3_s640_epochs.txt
      # Fixtures SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_make_sh3_scheduler_fixture.py <dump> --fb640 --emit` FIRST.
      FIXTURE="$(fix bios_sh3_s640 payload_sh3_s640)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_640=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  sh3_lpddr_fb_z)
      # Ch357 — PERSISTENT AUTHENTIC Z: the N-texture scheduler with a REAL per-fragment PSMZ16S depth test on an
      # LPDDR-persistent Z buffer. The plain color writer is replaced by gs_lpddr_zc_emit: per fragment it clamp16-reads
      # /GEQUAL-compares/updates a packed-16b Z line in LPDDR (Z base 0x140000) and emits color ONLY on Z-pass. Z PERSISTS
      # across the 3 scheduler epochs (precleared exactly once by clear_start at ARM; texture rebind does NOT clear it).
      # The three AXI streams share the LPDDR through the existing arbiters (color->wr_arb s0, Z-write->wr_arb s2,
      # Z-read->rd_arb s3). Strong-reject acceptance fixture (256x210, 18.31% reject: far-late fragments MUST be depth-
      # rejected). GS_SH3_LPDDR_FB_Z sets SCANOUT_LB 256x210 (6720 beats/frame). Bootlet (bios/payload_sh3_zsched) preloads
      # all 3 relocated CLUTs; the HOST streams each list at runtime (NO FEEDER_STG_INIT_FILE). ROP + arbitration + REAL-
      # RASTER integration all sim-proven: tb_top_psmct32_sh3_zrop (ROP golden), tb_top_psmct32_sh3_zarb (arbitration),
      # tb_top_psmct32_sh3_zint (real raster, ZERO fragment drops, Z+color bit-exact vs the clamp16 scoreboard).
      # BOARD ORDER (Codex host sequence): (1) wait setup/CLUT + feeder ready; (2) HPS PRECLEAR color 0x000000..0x0347FF
      # AND Z 0x140000..0x15A3FF via the write-probe; (3) per epoch upload the 512x512 PSMT8 texture @0x200000 + fill/verify
      # the cache; (4) ARM the writer (0x018[0]) -> clear_start preclears the Z buffer; WAIT clear_done before the first GO;
      # (5) per epoch stream the list + pulse GO (0x0E8) + confirm a FRESH frame_drained; Z persists across epochs; (6) after
      # the LAST drain raise video_src (0x018[2]=1) — scanout_lb (256x210) is forced. Read zc_g_drops (must be 0).
      # Fixtures SH3-derived -> LOCAL/gitignored: run `python3 tools/gs_make_sh3_scheduler_fixture.py <dump> --authz
      # --tag zsched --emit` FIRST (strong-reject draw list, authentic per-vertex Z).
      FIXTURE="$(fix bios_sh3_zsched payload_sh3_zsched)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_Z=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  sh3_lpddr_fb_z640)
      # Ch358 — PERSISTENT AUTHENTIC Z at NATIVE 640x480: the SAME strong-reject persistent-Z scene as sh3_lpddr_fb_z
      # (draws 8634/12757/145742, gs_lpddr_zc_emit, clamp16/GEQUAL, Z persists across epochs) but on the full-frame
      # native FB at AUTHENTIC screen coordinates (FBW=10, stride 2560, size 0x12C000, 38400 beats/frame; draws land at
      # x[407..619] y[97..306]). GS_SH3_LPDDR_FB_Z + GS_SH3_LPDDR_FB_640 together select SCANOUT_LB 640x480 AND
      # ZFB_PXW/H=640x480 (Z clear = 19200 beats). Map (gs_lpddr_map_pkg, disjoint by construction): color 0x0..0x12C000,
      # Z 0x140000..0x1D6000, tex 0x200000. Bootlet (bios/payload_sh3_zs640) preloads all 3 relocated CLUTs; the HOST
      # streams each list at runtime (NO FEEDER_STG_INIT_FILE). Sim gates: tb_top_psmct32_sh3_zs640_cap (capture +
      # fidelity oracle) and tb_top_psmct32_sh3_zint640 (real raster -> zc_emit -> REAL arbiters -> shared LPDDR,
      # ZERO drops, Z+color scoreboard-exact). Board proof = make -C sim sh3_zs640_board_compare (byte-for-byte vs
      # zint640 + replay-color 100%).
      # BOARD RUN (host unchanged — geometry comes from the epochs META):
      #   sudo ./ps2_sh3_sched --zbuf sh3_zs640_epochs.txt --dump-fb sh3_zs640_board_fb.mem
      # Fixtures SH3-derived -> LOCAL/gitignored: run `make -C sim sh3_zs640_fixture` FIRST.
      FIXTURE="$(fix bios_sh3_zs640 payload_sh3_zs640)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_Z=1)
$(mac GS_SH3_LPDDR_FB_640=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  sh3_lpddr_fb_z640c6)
      # Ch359 — shared-texture residency scene: six authentic SH3 draws grouped into two
      # 204-triangle epochs at native 640x480 persistent Z. The bootlet preloads the C6
      # relocated CLUT at CBP=13888; the host uploads/fills the shared texture once, then
      # verifies it remains resident for the reuse epoch.
      FIXTURE="$(fix bios_sh3_zs640c6 payload_sh3_zs640c6)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_Z=1)
$(mac GS_SH3_LPDDR_FB_640=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  sh3_lpddr_fb_z640_runtime_clut)
      # Ch367 -- runtime palette residency proof. Same native 640x480
      # persistent-Z geometry as Ch358. Keep the proven nonempty C6 setup
      # bootlet as a synthesis-retention anchor; the runtime test lists use
      # TEX0.CLD=0 and the HPS stages/checks every palette before each GO.
      # A zero-transfer bootlet lets Quartus prune the otherwise runtime-fed
      # texture/raster RAMs, so it is not a valid hardware proof vehicle.
      FIXTURE="$(fix bios_sh3_zs640c6 payload_sh3_zs640c6)"
      SCENE="$(mac GS_SH3_LPDDR_FB=1)
$(mac GS_SH3_LPDDR_FB_Z=1)
$(mac GS_SH3_LPDDR_FB_640=1)
$(mac GS_LPDDR_TEX=1)"
      ;;
  --list|"")
      echo "Profiles: lpddr_tex (Ch322 texture)  lpddr128_scanout (Ch321 scanout)  tile_spill (Ch323 spill/reload)  feeder (Ch330 command-list)  feeder_persp (Ch342 perspective)  feeder_cube (Ch343 authentic cube)  texalpha (Ch344 textured-alpha sprite)  feeder_sprite (Ch345a runtime sprite)  sh3_clut (Ch347 authentic SH3 PSMT8+CLUT)  sh3_persp (Ch348 SH3 PSMT8+CLUT perspective)  sh3_real_draw (Ch350/351/352 SH3 real-draw crop, LPDDR tex + CSM1 + widened persp)  sh3_lpddr_fb (Ch353 SH3 full 256x334 on an LPDDR-only PSMCT32 FB + line-buffer scanout, frame_drained-gated)  sh3_lpddr_fb_multi (Ch354 3 SH3 draws -> 256x338)  sh3_lpddr_fb_multitex (Ch355 2 DIFFERENT-texture draws -> 320x381, runtime-staged)  sh3_lpddr_fb_sched (Ch356 N-texture data-driven scheduler -> 384x381)  sh3_lpddr_fb_640 (Ch357 native 640x480 full-frame)  sh3_lpddr_fb_z (Ch357 PERSISTENT PSMZ16S Z, 256x210 strong-reject)  sh3_lpddr_fb_z640 (Ch358 PERSISTENT Z at NATIVE 640x480)  sh3_lpddr_fb_z640c6 (Ch359 shared-texture residency)  sh3_lpddr_fb_z640_runtime_clut (Ch367 runtime palette residency)"
      echo "Active profile:"
      sed -n '/DE25_PROFILE_SCENE/,/DE25_PROFILE_SCENE/p'     "$QSF" | grep VERILOG_MACRO || echo "  (no scene macros)"
      sed -n '/DE25_PROFILE_FIXTURE/,/DE25_PROFILE_FIXTURE/p' "$QSF" | grep IMAGE_FILE
      [ -z "$PROFILE" ] && exit 2 || exit 0
      ;;
  *) echo "unknown profile: $PROFILE (try --list)"; exit 2 ;;
esac

# Replace the content between each marker pair. Read the replacement from a file
# rather than `awk -v`: awk consumes backslash-escaped quotes in Verilog macros.
swap_block() { # $1=tag $2=replacement-file  (reads stdin QSF, writes stdout)
    awk -v tag="$1" -v repl_file="$2" '
        $0 ~ (">>> " tag " ") {
            print
            while ((getline line < repl_file) > 0) print line
            close(repl_file)
            inblk=1
            next
        }
        $0 ~ ("<<< " tag " <<<") { inblk=0 }
        !inblk { print }
    '
}

fixture_tmp="$(mktemp)"
scene_tmp="$(mktemp)"
tmp="$(mktemp)"
trap 'rm -f "$fixture_tmp" "$scene_tmp" "$tmp"' EXIT HUP INT TERM
printf '%s\n' "$FIXTURE" > "$fixture_tmp"
printf '%s\n' "$SCENE" > "$scene_tmp"
swap_block DE25_PROFILE_FIXTURE "$fixture_tmp" < "$QSF" \
  | swap_block DE25_PROFILE_SCENE "$scene_tmp" > "$tmp"

# sanity: both marker pairs must still be present (don't clobber the QSF on a bad run)
for tag in DE25_PROFILE_FIXTURE DE25_PROFILE_SCENE; do
    [ "$(grep -c "$tag" "$tmp")" -eq 2 ] || { echo "ERROR: marker '$tag' missing after edit — QSF unchanged"; rm -f "$tmp"; exit 1; }
done
mv "$tmp" "$QSF"
trap - EXIT HUP INT TERM
rm -f "$fixture_tmp" "$scene_tmp"
echo "Selected profile: $PROFILE"
echo "  fixture: $(echo "$FIXTURE" | grep -o '[a-z0-9_]*\.mem' | tr '\n' ' ')"
echo "  scene  : $(echo "$SCENE"   | grep -o 'GS_[A-Z0-9_]*=1' | tr '\n' ' ')"
echo "Now re-fit in Quartus."
