Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HIPIMS_Generator

FPGA core for a HIPIMS (High Power Impulse Magnetron Sputtering) pulse generator: a timing-critical VHDL design on a Cyclone IV E (EP4CE6E22C8) that drives a cathode half-bridge, up to 8 oscilloscope trigger/probe channels, and an anode-side SiC half-bridge with driver fault handling — all configured at runtime over SPI by a companion STM32 microcontroller.

(Русская версия: README_ru.md)

Status

Compiles clean through the full Quartus flow (quartus_mapquartus_fitquartus_asmquartus_sta), flashed and verified on real hardware together with the companion STM32 firmware: base pulse, a negative-delay (pre-trigger) probe channel, the anode PWM channel with its dead-time control, and fault handling have all been observed working on an oscilloscope. Still bring-up stage — not all probe channels or Leg B have been exercised on real hardware yet.

Architecture

The system is split across two repositories on purpose:

  • This repository (Quartus/VHDL, Cyclone IV E) — owns only what is actually timing-critical: the pulse generation core and an SPI slave register file. No UI, no persistence, nothing that isn't directly part of producing correctly-timed output signals.
  • A separate STM32F103C8T6 ("Blue Pill") PlatformIO project (HiPIMS_Menu, not in this repo) — owns the LCD1602 menu, rotary encoder input, flash-based parameter persistence, and acts as the SPI master that configures this FPGA at runtime.

Why the split

The UI (menu, LCD, encoder) was originally built entirely in VHDL on the FPGA. It worked and closed timing, but reached 54% LE usage and became code that was genuinely hard to safely modify by hand. Moving the UI to a microcontroller — where a menu system is cheap and ordinary C, not a timing-sensitive FSM competing for the same clock domain as the pulse core — dropped FPGA usage back down and made the split's boundary a simple one: the FPGA never has to be touched to change a menu, add a display, or add flash storage. Anything that isn't pulse timing belongs on the microcontroller side.

Signal path

STM32 (SPI master)                    FPGA (SPI slave, this repo)
  menu / encoder / LCD                  spi_slave_regs.vhd
  flash-persisted config      --SPI-->  register file (36 regs)
  CRC-8 + retry + status read           |
                                         v
                                  pulse_generator.vhd
                                  (shared time-base counter)
                                   |        |         |
                              leg_interlock  8x       anode_channel.vhd
                              (Leg A/Leg B)  pulse_channel  (SiC half-bridge,
                                   |         (probes)        fault/reset,
                                   v              v           own leg_interlock)
                              LEG_A_OUT/     PROBE_OUT[0..7]  ANODE_PWM_H/L,
                              LEG_B_OUT                       ANODE_RST_A/B_N

Channels

  1. Base pulse (Leg A / Leg B half-bridge) — drives an IR2213-class gate driver. Only Leg A is currently wired up; Leg B exists in the design (its own delay/width registers, same shoot-through interlock) but is unused, reserved for a future second leg.
  2. Probe/scope trigger channels — up to 8 (NUM_PROBE_CHANNELS, 5 currently wired), each independently enabled with its own delay (which may be negative, i.e. pre-trigger, down to −500µs) and width. All built from the same pulse_channel primitive as the legs.
  3. Anode channel — a second, independent half-bridge (SiC, e.g. driven through UCC21750/UCC21520-class isolated gate drivers) that runs a free-running PWM burst (configurable frequency/duty 50%/duration) gated to a window relative to the same shared start event as Leg A. Has its own dead-time-protected complementary output, two fault inputs, and two active-low reset-pulse outputs for the gate drivers' fault latches.

Time-base convention

Every time-valued register is a raw 50 MHz clock cycle count — not microseconds, not milliseconds. 1 LSB = 20 ns. raw_cycles = us * 50. This is deliberate: any runtime multiply/divide by a non-power-of-two constant on the FPGA synthesizes as an expensive lpm_mult/lpm_divide megafunction (one early register-file design accidentally consumed the entire DSP block budget this way). Unit conversion to human-readable values is the STM32 firmware's job, where it is free.

Negative pre-trigger delays (Leg B, probes, the anode channel) are resolved into an absolute position on an extended time-base window: [−MARGIN_PRE, period + MARGIN_POST), MARGIN_PRE = MARGIN_POST = 25000 cycles (500 µs). MAX_PERIOD_CYCLES = 2^23 − 1 − MARGIN_PRE − MARGIN_POST = 8 338 607 cycles (~166.77 ms) is the safe ceiling for REG_PERIOD — the FPGA does not validate this, the caller (STM32) must keep it in range.

Safety design

  • Enable and Start are two independent flags, not one. REG_ENABLE gates pulse generation internally. REG_START only drives a dedicated physical output pin — an external AND gate combines it with other permit signals before it can actually enable the power stage. They are not logically related to each other.
  • Leg A/Leg B shoot-through interlock (leg_interlock.vhd) is a small, formally-reasoned mutual-exclusion block: by construction, leg_a_out and leg_b_out are never both 1, and a leg only turns on after the other has been off for at least a configured dead time, Leg A having priority on a tie (proof sketch in the file's header comment). The anode channel reuses this exact block for its own H/L outputs (instantiated a second time) instead of writing new deadtime logic.
  • Anode dead time has a hardcoded, SPI-unreachable floor. REG_ANODE_DEADTIME only ever adds to a fixed 300 ns minimum (MIN_DEAD_TIME_CYCLES generic in anode_channel.vhd) — it cannot reduce dead time below that floor, including at its power-on-reset default of 0. Without this, an unconfigured register file (all zeros after reset, before the STM32 ever writes anything) would default to zero dead time, a real shoot-through risk.
  • Anode driver fault immediately gates the whole generator, not just the anode channel — effective_enable = global_enable AND NOT fault_any is used everywhere global_enable would otherwise be wired (Leg A, Leg B, all probes). A fault on the anode drivers most likely correlates with an arc in the discharge; there is no reason to keep the cathode pulse alive while it's latched.
  • Fault recovery is manual-restart-only. The fault latch does not clear itself once its cool-down (100 µs) + reset-pulse (2 µs) dwell completes — clearing requires an explicit rising edge on raw REG_ENABLE (0→1). An earlier version auto-cleared after the dwell, which raced the STM32's much slower SPI/software reaction time: the FPGA could silently relaunch generation before the firmware even saw the fault in the status byte. Requiring an edge makes "the next launch is issued by the operator, not automatically" true at the hardware level. A still-present physical fault immediately re-latches on the next attempt — expected, not a bug.
  • Any input pin that can be physically unconnected during bring-up gets a defined pull. ANODE_FAULT_A_N/ANODE_FAULT_B_N are active-low with a weak pull-up, so "no gate driver wired up yet" reads as a deterministic "no fault" instead of an undefined, noise-susceptible floating level (this was a real regression once the fault latch above stopped self-clearing — a floating pin could latch the whole generator off permanently).
  • SPI receiver has a glitch filter, separate from (and in addition to) the double-flop metastability synchronizer: a candidate value on SCK/MOSI/CS_N must hold for 4 consecutive CLK50 cycles (80 ns) before being accepted, added after real switching noise from the anode channel was observed corrupting SPI framing on the bench.

SPI protocol (v2)

STM32 is the SPI master, the FPGA is the slave. Mode 0 (CPOL=0, CPHA=0), MSB-first. One register write is a 7-byte unit, and several units may be sent back-to-back under one CS_N low pulse:

Byte Content
0 register address, 0..NUM_REGS-1 (36 registers). Out-of-range is accepted for framing but never committed.
1–4 32-bit signed value, big-endian
5 CRC-8 over bytes 0–4. Poly x^8+x^2+x^1+1 (0x07), init 0x00, no reflection, no final XOR (the common "CRC-8"/SMBus PEC variant). On mismatch the write is dropped.
6 master sends a dummy byte while simultaneously reading a status byte back over MISO: bit0 = ACK (CRC matched and, if the address was in range, the write committed), bit1 = fault_a live, bit2 = fault_b live, bits 3–7 reserved.

On NACK, the caller must re-send the same 7-byte unit after toggling CS_N high and back low — the FPGA's bit/byte counters only reset on CS_N high, so resending inside the same CS session cannot recover from a framing desync caused by a glitch on SCK. The CS_N high pulse between retries must be noticeably longer than the 80 ns glitch-filter threshold. The FPGA never retries anything itself.

Register map (rtl/spi_regs_pkg.vhd, NUM_REGS = 36)

Addr Register Notes
0 REG_ENABLE 0/1, gates generation internally
1 REG_START 0/1, drives the physical permit pin only; unrelated to ENABLE
2 REG_PERIOD raw cycles; keep ≤ MAX_PERIOD_CYCLES (8 338 607)
3 REG_LEGA_WIDTH raw cycles
4 REG_LEGB_ENABLE 0/1
5 REG_LEGB_DELAY signed raw cycles, relative to Leg A's start
6 REG_LEGB_WIDTH raw cycles
7–30 8 probe channels × 3 channel i (0–7): enable = 7+i*3, delay (signed) = 7+i*3+1, width = 7+i*3+2
31 REG_ANODE_ENABLE 0/1
32 REG_ANODE_PERIOD raw cycles; PWM period = value + 1
33 REG_ANODE_DELAY signed raw cycles, relative to the shared start event
34 REG_ANODE_DURATION raw cycles
35 REG_ANODE_DEADTIME raw cycles, addition on top of the fixed 300 ns floor — not the absolute value

Pinout

Board: a generic Chinese "SANKAI" EP4CE6E22C8N (TQFP144) breakout board; most FPGA pins are broken out to headers labeled with the real pin number on the silkscreen.

Signal Pin Notes
CLK50 24 50 MHz oscillator
SPI_SCK 144
SPI_MOSI 143
SPI_MISO 142
SPI_CS_N 141 SPI signals share one contiguous pin run, bundled together on purpose for noise immunity
LEG_A_OUT 99
LEG_B_OUT 98 unused leg, reserved
PROBE_OUT[0..7] 85, 86, 77, 83, 76, 28, 30, 31 5 currently wired
START_OUT 32 physical permit pin
ANODE_FAULT_A_N 33 input, active-low, weak pull-up
ANODE_FAULT_B_N 34 input, active-low, weak pull-up
ANODE_RST_A_N 100 output, active-low reset pulse
ANODE_RST_B_N 104 output, active-low reset pulse
ANODE_PWM_H 138
ANODE_PWM_L 137

105/106/103 are free (formerly SPI, moved off 2026-08-18). The Quartus fitter reserves pins 6, 8, 12, 13, 101 for Active Serial configuration — avoid them for any new signal.

Repository layout

rtl/
  reset_gen.vhd          power-on reset generator
  pulse_gen_pkg.vhd       shared types/constants (time_t, delay_t, TIME_WIDTH, margins)
  pulse_channel.vhd       generic [start, start+width) window primitive
  leg_interlock.vhd       shoot-through-safe complementary drive (shared by legs + anode)
  pulse_generator.vhd     time-base counter, Leg A/B, probes, anode instantiation, effective_enable
  spi_regs_pkg.vhd        register address map, NUM_REGS
  spi_slave_regs.vhd      SPI slave: sync, glitch filter, CRC-8, MISO status byte
  anode_channel.vhd       anode PWM burst + fault/reset handling
  Generator.vhd           top-level: wires SPI registers into pulse_generator

  i2c_master.vhd, lcd1602_pcf8574.vhd, quad_encoder.vhd,
  menu_pkg.vhd, menu_fsm.vhd, menu_lcd_render.vhd
                          superseded by the STM32 split — kept on disk,
                          NOT part of the build (see Generator.qsf)

techdocs/ holds the working design log and is not tracked in this repository (see .gitignore) — it's internal notes, not published documentation.

Building

Quartus Prime 24.1std, target EP4CE6E22C8. From the project root:

quartus_map Generator
quartus_fit Generator
quartus_asm Generator
quartus_sta Generator

Current build: 2552/6272 logic elements (41%), 0/30 DSP blocks, worst-case setup slack ≈8.5 ns (Slow 1200 mV 0°C model) on a 20 ns clock.

Companion project

The STM32 firmware (menu, encoder, LCD, SPI master, flash persistence) lives in a separate PlatformIO project, not part of this repository: HiPIMS_Menu.

About

SPI-configurable HIPIMS pulse generator core for Cyclone IV E FPGA (VHDL) — dual-leg interlocked drive, anode PWM channel, fault-latch safety, paired with an STM32 SPI-master front-end.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages