Learn how to implement a circular LED shifter using pure programmable logic on the ZCU104 MPSOC board. This tutorial covers VHDL implementation, clock management, synchronized reset handling, and proper I/O planning. We demonstrate practical hardware-software integration while focusing exclusively on programmable logic capabilities.
Key Learning Outcomes:
- VHDL module implementation for LED control
- Integration of VHDL code in IP block design
- ZCU104 carrier card resource utilization
- Clock management using Clocking Wizard
- Synchronized reset implementation with Processor System Reset IP
You can watch the full Video here:
Block diagram of designThis block diagram shows a hardware design to control 4 LEDs in a circular shifting manner on an XCZU7EV MPSoC FPGA board, with the shift direction controlled by two push buttons. Here's a detailed explanation of each component:
Push ButtonsPush-buttons SW17, SW14, and SW15:
- SW17: Connected to the Reset input of the LED_Shifter Module via Processor system reset. Pressing SW17 resets the LED shift pattern and direction to default values.
- SW14 (B4): This push button is connected to the btn_left input of the LED_Shifter Module. Pressing SW14 changes the shift direction to left.
- SW15 (C4): This push button is connected to the btn_right input of the LED_Shifter Module. Pressing SW15 changes the shift direction to right.
- IDT Clock Generator (U182): Provides differential clock signals (AH18 and AH17) to the Clocking Wizard block.
- Clocking Wizard: This block generates a stable 100 MHz clock signal from the differential input provided by the clock generator. The 100 MHz clock is then fed into the clk input of the LED_Shifter Module, which controls the timing for the LED shift.
- Processing System Reset: This block generates a synchronized reset signal to initialize the VHDL module. Then reset signal is connected to the reset input of the LED_Shifter Module.
This module is the core logic controlling the LEDs, based on the push button inputs and the 100 MHz clock. Its key inputs and outputs are:
- clk: The 100 MHz clock signal from the Clocking Wizard, which sets the timing for LED shifting every 250ms.
- reset: Resets the module, setting the LED shift pattern to a default value and shifting direction to the right.
- btn_left and btn_right: Inputs from push buttons SW14 and SW15, which toggle the LED shift direction. A single press on btn_left sets a left shift, while btn_right sets a right shift.
- LED Outputs (Led[3:0]): These signals control the state of the 4 LEDs (DS39, DS40, DS37, and DS38). The LED states shift in a circular pattern with each 250ms interval, based on the selected direction.
The LEDs DS39, DS40, DS37, and DS38 are connected to the outputs of the LED_Shifter Module:
- A5 (Led[3]): Controls LED DS39.
- B5 (Led[2]): Controls LED DS40.
- D6 (Led[1]): Controls LED DS37.
- D5 (Led[0]): Controls LED DS38.
Each LED is turned on or off based on the current state of the LED pattern from the LED_Shifter Module. The LEDs shift in sequence every 250ms in a circular manner, either left or right depending on the push button input.
I/O planningThe ZCU104 board offers various control and I/O interfaces, including user LEDs. The board has four user LEDs, each located near a push button. In our design, these four LEDs will blink in a circular manner once the bitstream is loaded, utilizing all available user LEDs.
The design also includes two pushbuttons to control the shift direction in the VHDL module. Pressing one of these buttons changes the shift direction to either left or right, depending on which button is pressed.
Additionally, two pushbuttons are used for reset purposes:
Clock Wizard Reset: Holding this button stops the Clocking Wizard, which in turn halts the LED shifting.
VHDL Module Reset: Pressing this button resets the shift value to its initial state.
Save the following VHDL code as LED_Shifter.vhd in your project directory.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity LED_Shifter is
generic (
COUNTER_MAX : integer := 25000000 -- 100 MHz clock, 250ms (100e6 * 0.25)
);
Port (
clk : in std_logic; -- 100 MHz clock
reset : in std_logic; -- Reset signal
btn_left : in std_logic; -- Push button to select left shift
btn_right : in std_logic; -- Push button to select right shift
leds : out std_logic_vector(3 downto 0) -- 4 LEDs
);
end LED_Shifter;
architecture Behavioral of LED_Shifter is
signal led_reg : std_logic_vector(3 downto 0) := "0001"; -- Starting LED pattern
signal counter : integer := 0;
signal shift_enable : std_logic := '0';
signal shift_direction : std_logic := '0'; -- '0' for right, '1' for left
signal btn_left_pressed : std_logic := '0';
signal btn_right_pressed : std_logic := '0';
begin
-- Process to control the timing (250ms)
process(clk, reset)
begin
if reset = '1' then
counter <= 0;
shift_enable <= '0';
elsif rising_edge(clk) then
if counter = COUNTER_MAX - 1 then
counter <= 0;
shift_enable <= '1'; -- Enable shift after 250ms
else
counter <= counter + 1;
shift_enable <= '0';
end if;
end if;
end process;
-- Process to change the direction based on button presses
process(clk, reset)
begin
if reset = '1' then
shift_direction <= '0'; -- Default to right shift
btn_left_pressed <= '0';
btn_right_pressed <= '0';
elsif rising_edge(clk) then
-- Detect left button press and toggle direction to left
if btn_left = '1' and btn_left_pressed = '0' then
shift_direction <= '1'; -- Set to left shift
btn_left_pressed <= '1'; -- Debounce mechanism
elsif btn_left = '0' then
btn_left_pressed <= '0'; -- Button released
end if;
-- Detect right button press and toggle direction to right
if btn_right = '1' and btn_right_pressed = '0' then
shift_direction <= '0'; -- Set to right shift
btn_right_pressed <= '1'; -- Debounce mechanism
elsif btn_right = '0' then
btn_right_pressed <= '0'; -- Button released
end if;
end if;
end process;
-- Process for shifting the LEDs
process(clk, reset)
begin
if reset = '1' then
led_reg <= "0001"; -- Reset LED pattern to first LED on
elsif rising_edge(clk) then
if shift_enable = '1' then
if shift_direction = '1' then
-- Left circular shift
led_reg <= led_reg(2 downto 0) & led_reg(3);
else
-- Right circular shift
led_reg <= led_reg(0) & led_reg(3 downto 1);
end if;
end if;
end if;
end process;
leds <= led_reg; -- Assign the current LED pattern to the output
end Behavioral;
Steps to Create the Vivado Design:Create a New Project:
- Create a new project with ZCU104 and create a new block design for it.
Add Programmable Differential Clock (300MHz):
- From the “Board” tab list, add a Programmable Differential Clock (300MHz). This will add a clock wizard with a differential input and connect it to the appropriate external pins.
Add Reset
- From the “Board” tab list, add a reset and connect it to the reset pin of the clocking wizard.
Note that when using the board examples, the board files’ pre-setups are automatically used. For example, based on the board pre-setups, the differential input is connected to I/O pins AH17 and AH18, which are connected to the 300MHz clock, and the reset input is connected to the M11 I/O pin, which is the CPU reset pushbutton.
Add VHDL code as a source to projectNow we are going to first add the VHDL code to the Vivado project sources. Click on add resource button and then select LED shifter VHDL file, it will add the VHDL design beside other resources.
Add VHDL module like an IP to your designRight click on the block design, and select “add module”. From provided list, you should be able to select the LED shifter module.
It will add the module to the block design, and allows us to work with our VHDL design like a normal IP block.
- You can fine Processor System Reset in Vivado IP catalogue and add it to your design.
- Connect the slowest sync clock to the output of the Clocking Wizard.Connect the active-high pin of the Processor System Reset block to the reset pin of the VHDL module, as the reset in the VHDL code is designed as active-high.
- Set the external reset input pin as an external pin.Double-click on the created pin and configure it as active-high, as pressing the external pushbutton will pull the pin up.
- Make the input pins of the VHDL module as external.
- Make the LED outputs external as well. Note that the LED output bus has a width greater than 0, so each bit of this bus will become a separate output pin.
- Connect the clock pin of module to 100MHz clock.
Please note that if you make the reset pin of VHDL module directly as an external pin the Vivado will return following timing error. The main job of Processor System Reset IP is to provide the synchronized reset and remove any timing issue for reset signal.
Validate the design and add the top wrapper. and let the Vivado to manage and auto handle it.
Synthesis the designRun Synthesis design and Wait until the Synthesis is finished
I/O planningOpen Synthesized design and Open I/O planning.
- Assign the proper pin for each bit of LEDs bus.
- Assign the proper pin for button left and pins
Assign the proper pin for processor reset external pin.
Save the I/O setup as a constraint file. This constraint file will be added into the source directory.
You can also fulfill all the I/O planning by adding following constraint file to your project:
set_property PACKAGE_PIN A5 [get_ports {leds_0[3]}]
set_property PACKAGE_PIN B5 [get_ports {leds_0[2]}]
set_property PACKAGE_PIN D6 [get_ports {leds_0[1]}]
set_property PACKAGE_PIN D5 [get_ports {leds_0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_0[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_0[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {leds_0[0]}]
set_property PACKAGE_PIN B4 [get_ports btn_left_0]
set_property PACKAGE_PIN C4 [get_ports btn_right_0]
set_property IOSTANDARD LVCMOS33 [get_ports btn_left_0]
set_property IOSTANDARD LVCMOS33 [get_ports btn_right_0]
set_property PACKAGE_PIN B3 [get_ports ext_reset_in_0]
set_property IOSTANDARD LVCMOS33 [get_ports ext_reset_in_0]
Now you are ready to generate the bitstream.
Download the bitstream to the deviceWith our bitstream is ready, it's time to run it on the actual hardware. First, let's connect our ZCU104 board. We'll need to connect the power supply, the JTAG USB connection for programming.
Before we program the FPGA, we need to set the correct boot mode. The ZCU104 has several boot options, controlled by a set of DIP switches on the board. For now, we'll use JTAG boot mode, which allows us to program the FPGA directly from Vivado.
Now, let’s Download the bitstream into the device over the USB JTAG.
Obtained results- First, notice the initial LED pattern, as you can see the User LEDs are blinking in a circular manner. And Watch as it shifts right by default. The 250ms timing creates this smooth, visible movement
- press the left and right button and see how the direction changes
- Now, press the VHDL module’s reset button, the LEDs will back to its starting position.
- When the CPU reset is pressed, the clocking wizard is also reset. As long as the push button is held down, the clocking wizard remains in the reset state, preventing any output clocks from being generated, which causes the leads to stop blinking. Once the reset is released, the clocking wizard resumes clock generation and the leads continue blinking.
Comments
Please log in or sign up to comment.