# QX-250 FPGA — RTL & Verification Deterministic hardware layer for the QX-250 flight controller. See [`../07_FPGA_Architecture.md`](../07_FPGA_Architecture.md) for the full block spec (B1–B12). ## Layout ``` fpga/ ├── rtl/ │ ├── dshot600_tx.sv # B1 single-channel DShot600 serializer │ ├── dshot600_tx_x4.sv # B1 4-channel wrapper → DSHOT_M1..M4 │ ├── clk_reset.sv # B8 TOP: clk_sys + rst_n generation │ ├── qx_pll.sv # B8 PLL wrapper (behavioral sim + vendor stubs) │ ├── reset_sync.sv # B8 async-assert / sync-deassert reset │ ├── cdc_sync2.sv # B8 2-flop input synchronizer │ ├── spi_master.sv # B3 SPI byte engine (mode 3, 8 MHz) │ ├── imu_spi.sv # B3 MPU-6000 sequencer (init writes + burst read) │ ├── sample_sync.sv # B5 timestamp + sample ticks (8 kHz / 50 Hz / 1 µs) │ ├── spi_slave_regfile.sv # B7 host register interface (SPI slave, ADR-001) │ ├── safety_interlock.sv # B6+B9 arm/failsafe gate + watchdog + LED/buzzer │ ├── i2c_master.sv # B4 I2C byte engine (open-drain, 400 kHz) │ ├── baro_i2c.sv # B4 DPS310 sequencer (config + P/T read) │ └── erpm_decode.sv # B2 bidir-DShot eRPM GCR/CRC decode datapath ├── docs/ │ └── ADR-001-host-interface.md # decision: SPI slave (not AXI) + register map └── sim/ ├── mpu6000_bfm.sv # MPU-6000 SPI slave bus-functional model (sim only) ├── tb_dshot600_tx.sv ├── run.do # B1 ├── tb_clk_reset.sv ├── run_clk_reset.do # B8 ├── tb_imu_spi.sv ├── run_imu_spi.do # B3 ├── tb_sample_sync.sv ├── run_sample_sync.do # B5 ├── tb_spi_slave_regfile.sv ├── run_regfile.do # B7 ├── tb_safety_interlock.sv ├── run_safety.do # B6+B9 (incl. SVA) ├── dps310_i2c_bfm.sv ├── tb_baro_i2c.sv ├── run_baro.do # B4 (bring-up) ├── tb_erpm_decode.sv ├── run_erpm.do # B2 (decode path) └── run_all.do # compile + run ALL blocks ``` ## Run in Questa ``` cd fpga/sim vsim -c -do run_all.do # regress every block # or individually: vsim -c -do run.do # B1 vsim -c -do run_clk_reset.do # B8 vsim -c -do run_imu_spi.do # B3 vsim -c -do run_sample_sync.do # B5 vsim -c -do run_regfile.do # B7 vsim -c -do run_safety.do # B6+B9 vsim -c -do run_erpm.do # B2 (eRPM decode datapath) vsim -c -do run_baro.do # B4 (bring-up — see note) ``` Each testbench is self-checking and ends with `RESULT: PASS (N checks, 0 errors)`. - **B1** measures each bit's HIGH width on the pin, rebuilds the 16-bit frame, checks throttle/telemetry/CRC and DShot600 timing. - **B8** checks the 48 MHz period, lock-gated reset release, async reset, 2-flop sync latency. - **B3** runs INIT then a burst read against the MPU-6000 BFM, checks decoded accel/temp/gyro, the config writes, and slow-then-fast SCLK. - **B5** checks the µs/IMU/baro tick periods, timestamp increment, and sample-timestamp latching. - **B7** an SPI-master BFM (the MCU) exercises the register map: WHOAMI, sensor burst read, motor writes + auto-increment readback, arm/heartbeat. - **B6+B9** arm/disarm/lockout/watchdog scenarios + two **concurrent SVA** safety properties (`!armed ⇒ motors 0`; `motor hot ⇒ armed`) — the same assertions are provable with Questa formal. ## Status | Block | RTL | TB | Pre-validation (no HDL sim at draft time) | |---|---|---|---| | B1 DShot600 TX | ✅ | ✅ | Python model, 7 vectors PASS | | B8 clk/reset/PLL | ✅ | ✅ | Python model, reset/sync timing PASS | | B3 IMU SPI + BFM | ✅ | ✅ | Edge-level Python model (byte engine + sequencer) PASS | | B5 sample-sync | ✅ | ✅ | logic pre-checked; period/latch TB ready | | B7 host regfile | ✅ | ✅ | Python protocol model, 13 checks PASS (see ADR-001) | | B6+B9 safety interlock | ✅ | ✅ | FSM model, 14 checks + invariant PASS; SVA formal-ready | | B2 eRPM decode (datapath) | ✅ | ✅ | GCR/CRC round-trip model, 16 vectors + reject PASS | | B4 baro I2C | ✅ | ⚠ bring-up | byte/ACK sequence PASS; I2C bit-phase to confirm in sim | **Verification maturity — read this.** Blocks B1/B8/B3/B5/B7/B6+B9 and the B2 *decode datapath* were each transcribed from a cycle-/edge-accurate model that already passes, so they should `RESULT: PASS` on first `vsim`. **B4 is different:** its transaction/ACK *sequence* is validated, but the I2C SCL/SDA *bit-phase* engine + DPS310 BFM were written without a matching low-level model — treat B4 as first-sim bring-up (if it doesn't pass, single-step SCL/SDA around the address ACK). **B2's analog line-capture front-end** (×4 bidir-DShot line sampling that produces `sent`) is likewise not yet built — only the decode datapath is. Behavioral PLL is used in sim; define `VENDOR_POLARFIRE` / `VENDOR_LATTICE` and complete the primitive in `qx_pll.sv` for synthesis. ## Design notes - **48 MHz `clk_sys`** makes DShot600 land on exact integer cycle counts (BIT=80, T1H=60, T0H=30) — zero timing rounding error. - RTL is **vendor-neutral** (no hard primitives); the PLL and I/O buffers live in B8, outside these modules, so the PolarFire / Lattice choice stays reversible. - Set `BIDIR=1` for bidirectional DShot (inverted CRC) once B2 eRPM capture lands. ## Next blocks B8 clk/reset → (B1 ✓) → B3 IMU SPI + B5 timestamp → B7 register interface → B6/B9 safety interlock. Build order per §8 of the architecture doc.