Skip to content

Basys 3 I/O recipes

Use the official master XDC as the authority for pin locations and I/O standards. Uncomment only the signals you use, then rename each get_ports argument to match your top entity.

Switches and LEDs

Switches are asynchronous to the 100 MHz system clock. For a purely combinational switch-to-LED experiment, direct wiring is fine. If a switch affects a state machine or counter, synchronize it first.

led_o <= switch_i;

Switches bounce less than pushbuttons but still do not change synchronously with the clock.

Pushbuttons

Mechanical buttons present two separate problems:

  1. Asynchronous timing: the electrical transition can occur near any clock edge.
  2. Bounce: one press creates several rapid transitions over milliseconds.

Pipeline:

physical button → 2-flop synchronizer → debouncer → edge detector → application

Do not treat a raw button as a clock.

One-cycle rising-edge pulse

process(clk_i)
begin
  if rising_edge(clk_i) then
    previous_q <= clean_button_i;
    pulse_o    <= clean_button_i and not previous_q;
  end if;
end process;

Four-digit seven-segment display

The four digits share segment lines. Only the digit-enable lines are separate, so the controller must multiplex:

  1. Select one digit.
  2. Drive the seven segment pattern and decimal point.
  3. Wait a short scan interval.
  4. Select the next digit.

At a sufficiently high refresh rate, persistence of vision makes all digits appear continuously lit.

The Basys 3 segment and digit signals are active-low. Confirm polarity in the reference manual and master XDC.

Hexadecimal decoder

with hex_i select
  segments_o <=
    "1000000" when x"0",
    "1111001" when x"1",
    "0100100" when x"2",
    "0110000" when x"3",
    "0011001" when x"4",
    "0010010" when x"5",
    "0000010" when x"6",
    "1111000" when x"7",
    "0000000" when x"8",
    "0010000" when x"9",
    "0001000" when x"A",
    "0000011" when x"B",
    "1000110" when x"C",
    "0100001" when x"D",
    "0000110" when x"E",
    "0001110" when x"F",
    "1111111" when others;

This table assumes bit order abcdefg and active-low outputs. If your vector uses gfedcba, reverse the mapping deliberately.

Scan-rate calculation

For four digits with a desired full-display refresh of 1 kHz:

  • Each digit is active once every 1 ms.
  • Digit selection changes at 4 kHz.
  • At 100 MHz, the selection interval is 25,000 clock cycles.

Use a clock enable at 4 kHz, not a 4 kHz logic-generated clock.

RGB LEDs

Each color channel is independently driven. Brightness control uses PWM:

red_o <= '1' when pwm_counter_q < red_level_i else '0';

Check whether the board connection makes the channel active-high or active-low before interpreting the result.

UART

UART is an excellent first serial interface because it uses only transmit, receive, and a shared baud-rate convention.

For a transmitter:

  1. Idle line is high.
  2. Send one low start bit.
  3. Send data least-significant bit first.
  4. Optionally send parity.
  5. Send one or more high stop bits.

For 115,200 baud at 100 MHz, the ideal clocks per bit are approximately:

100,000,000 / 115,200 = 868.0556

A simple integer divider uses 868 clocks/bit and has small baud error. More accurate designs use an accumulator or oversampling strategy.

The receive signal is asynchronous and must be synchronized before sampling. A robust receiver oversamples near the center of each bit.

VGA overview

VGA requires:

  • A pixel clock or clock enable satisfying the chosen mode.
  • Horizontal and vertical counters.
  • Horizontal and vertical sync pulses.
  • Active-video region detection.
  • Red, green, blue pixel values.

The Basys 3 uses resistor-ladder color outputs. Treat a VGA controller as an intermediate project because timing counters, generated clocking, and monitor compatibility must all agree.

Pmod connectors

Pmods expose FPGA I/O directly. Before connecting hardware:

  • Verify voltage and electrical standard.
  • Identify input/output direction.
  • Ensure grounds are shared.
  • Do not exceed FPGA pin current.
  • Add a synchronizer for asynchronous digital inputs.
  • Add timing constraints for synchronous external interfaces.

Board-interface principle

Put all physical concerns in a thin board-level entity:

pins/XDC ↔ board_top ↔ synchronized logical interface ↔ reusable core

Your reusable core should not need to know that a button bounces or a seven-segment anode is active-low.