Good news: using an external pull-up [resistor](https://www.onzuu.com/category/resistors) with an [FPGA](https://www.ampheo.com/c/fpgas-field-programmable-gate-array) is simple. The trick is to think in terms of who drives the line and when you let the resistor do its job. ![usb-fpga-2.14-1600](https://hackmd.io/_uploads/Bk8kQ-XbWx.jpg) I’ll split it into the two common cases: 1. Input pin with external pull-up (buttons, status lines, etc.) 2. Open-drain / wired-OR signal with external pull-up (I²C-style, interrupt lines, etc.) **1. Input pin with an external pull-up** **Hardware:** * Signal pin ↔ FPGA input * Same signal pin → resistor (e.g. 10 kΩ) → VCC * Optional: button / switch to GND, or an open-collector output from another device. **What happens electrically:** * When nobody pulls the line low → resistor pulls it HIGH. * When button/device pulls the line to GND → the input reads LOW. **FPGA side (HDL):** Just declare the pin as a normal input; nothing special in the code: ``` // Verilog input wire btn_in; // externally pulled up ``` ``` -- VHDL btn_in : in std_logic; -- externally pulled up ``` You then interpret it in logic, e.g. active-low button: `wire button_pressed = (btn_in == 1'b0);` **Constraints / pin settings:** * Map btn_in to the correct package pin. * IO standard: e.g. LVCMOS33. * Usually disable the internal pull-up (you already have one outside). If you leave it on, it just forms a parallel resistor (not fatal, but wastes current and changes the effective pull-up value). Example (Xilinx .xdc): ``` set_property PACKAGE_PIN W5 [get_ports btn_in] set_property IOSTANDARD LVCMOS33 [get_ports btn_in] set_property PULLUP FALSE [get_ports btn_in] ``` **2. Open-drain / shared line with external pull-up** This is the more interesting case. Here the external pull-up defines HIGH, and one or more devices (including the FPGA) can pull the line LOW. Example use-cases: * I²C-like bus * Shared interrupt line (multiple devices can pull low) * “READY/BUSY” lines wired-OR with other devices **Hardware:** * Signal ↔ FPGA pin * Signal → resistor → VCC * Possibly several other devices on the same net that can pull it low. **Key idea for FPGA:** You never drive a 1 against the pull-up. You either: * Drive 0 → force the line LOW * Drive Z (high-impedance) → let the pull-up make it HIGH **2.1 Verilog open-drain pattern** ``` // Top-level inout wire od_line; // open-drain line with external pull-up reg drive_low; // 1 = pull low, 0 = release wire line_in; // what we read from the pin // Drive logic: 0 or Z, never 1 assign od_line = drive_low ? 1'b0 : 1'bz; // Read back the line assign line_in = od_line; ``` Usage: * To output LOW: drive_low <= 1'b1; * To output HIGH: drive_low <= 1'b0; (tri-state, pull-up takes it high) * To read the bus: use line_in. Constraint example (Xilinx): ``` set_property PACKAGE_PIN U4 [get_ports od_line] set_property IOSTANDARD LVCMOS33 [get_ports od_line] set_property DRIVE 8 [get_ports od_line] ; drive strength for low set_property SLEW SLOW [get_ports od_line] set_property PULLUP FALSE [get_ports od_line] ; because we use external ``` **2.2 VHDL open-drain pattern** ``` -- Top-level od_line : inout std_logic; signal drive_low : std_logic; signal line_in : std_logic; -- Drive logic od_line <= '0' when drive_low = '1' else 'Z'; -- Read back line_in <= od_line; ``` **3. When not to use open-drain style** If the external pull-up is just there for safety and the FPGA is the only active driver on that net, you can treat it as a normal push-pull output: * Configure the pin as a plain output. * Drive 0 and 1 as usual. * The resistor is then basically just a weak “failsafe” if the pin is high-Z during reset. But if another device may pull it low, then you must treat it as open-drain (0/Z only) to avoid bus contention ([FPGA](https://www.ampheoelec.de/c/fpgas-field-programmable-gate-array) driving 1 while someone else drives 0 → big current spike). **4. Quick checklist** To “use an externally pulled-up resistor in FPGA” correctly: **1. Decide the role of the pin:** * Input only → just declare it as input; external circuitry + pull-up define the level. * Open-drain I/O → use inout and drive 0/Z, never 1. **2. Set constraints:** * Correct package pin + I/O standard. * Turn off internal pull-up if you already have an external one (optional but cleaner). **3. Check the circuit:** * Resistor size in a sensible range (e.g. 2.2–10 kΩ depending on speed/fanout). * Ensure no active driver ever forces HIGH against someone else forcing LOW (unless you intentionally design a push-pull bus and guarantee exclusive ownership).