Skip to content

Wire fpga_rst_n into reset synchronizer (deferred from May 9 session) #3

@Abraxas3d

Description

@Abraxas3d

rtl: wire fpga_rst_n into reset synchronizer (deferred because it needs netlist-level investigation)

The STM32 drives PD0 to J3 pin 18A to FPGA site 18 to fpga_rst_n, but the current VHDL ignores this signal entirely (reset comes only from a 3-stage POR shifter initialized to all-ones). The STM32's sic_reset() toggles PD0, but the FPGA doesn't listen.

Two attempts on May 9 to wire fpga_rst_n into an async-assert / sync-deassert reset synchronizer both failed despite seemingly correct design.

Attempt 1 added the synchronizer with fpga_rst_n driving the async path:

vhdlprocess(clk_sys, fpga_rst_n)
begin
    if fpga_rst_n = '0' then
        reset_sync <= (others => '1');
    elsif rising_edge(clk_sys) then
        reset_sync <= reset_sync(1 downto 0) & '0';
    end if;
end process;
reset <= reset_sync(2);

Symptom: FPGA visibly alive (LEDs cycling, heartbeat present), SPI reads returned all zeros across all 17 frame bytes.

STM32 confirmed driving fpga_rst_n high, scope showed 3.3 V solid on J3 pin 18A.

Attempt 2 same VHDL plus PDC internal pull-up on the input pin:

tclldc_set_port -iobuf {IO_TYPE=LVCMOS33 PULLMODE=UP} [get_ports fpga_rst_n]
Symptom: strictly worse — all FPGA LEDs dark, including the heartbeat. Heartbeat counter has no reset clause in its sensitivity list and should always count when clk_sys is alive, so its silence indicates something deeper than reset semantics.

Possible root causes worth investigating:

  1. iCE40 SLICE SR-mode packing. Each iCE40 SLICE has one SR signal shared between both flops in it. If LSE packed the new async-set reset_sync flops into a SLICE that also contains unrelated flops, those other flops would inherit async-set semantics and be forced to '1' when fpga_rst_n='0'. (Same kind of optimization-induced rot we saw on pluto_msk.) LSE should respect this constraint and pack heterogeneous-SR flops separately, but corner cases exist.

  2. fpga_rst_n needs its own pre-synchronizer before feeding the SR input. Driving an iCE40 SLICE SR pin directly from an asynchronous external signal may not meet the architecture's recovery/removal timing. Standard practice is two dedicated flops to synchronize fpga_rst_n itself before using it as the SR input.

  3. Resynthesis-induced timing closure regression elsewhere in the design. Resynthesis can subtly affect routing, packing, and timing closure for parts of the design unrelated to the change itself. No visibility into what changed in the netlist between the working build and the broken one.

To redo cleanly (estimated half-day):

  1. Build with VHDL change but no pull-up. Diff .mrp (resource utilization) and .twr (timing) against working baseline. Look for register packing changes or new timing violations.

  2. Use Radiant netlist browser on the post-PAR netlist. Verify reset_sync flops and channelizer flops don't share SR signals inappropriately.

  3. Add explicit pre-synchronizer in VHDL:

vhdl   signal fpga_rst_n_meta, fpga_rst_n_sync : std_logic;
   -- ... clocked process syncing fpga_rst_n into clk_sys domain ...

  • Feed fpga_rst_n_sync to the SR input, not the bare pin.
  • Add PDC pull-up: ldc_set_port -iobuf {IO_TYPE=LVCMOS33 PULLMODE=UP} [get_ports fpga_rst_n]
  • Resynth, verify on hardware before committing.

Reference history: Working bitstream MD5 with no fpga_rst_n wiring: 1d2df577cdb1e5936ce13812bd29c5ac. Attempt 1 bitstream (broken, SPI zeros): produced different MD5. Attempt 2 bitstream (worse, all dark): 18f969b915bc69331d37c70012eaec92.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request
No fields configured for Feature.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions