FPGAs (Field-Programmable Gate Arrays) are unique compared to traditional microcontrollers because they do not execute sequential instructions. Instead, they consist of an array of configurable logic blocks that can be reprogrammed to perform custom digital logic functions. This allows FPGAs to execute multiple operations in parallel, making them highly efficient for specific tasks like signal processing, data manipulation, and real-time control.
To define how an FPGA functions, we use a Hardware Description Language (HDL), which specifies how the logic elements should be configured and interconnected. In this project, I will be using SystemVerilog, an advanced version of Verilog that introduces additional features like enhanced type safety, assertions, and object-oriented programming capabilities, making it more powerful for FPGA design.
For hardware, I am using the Arty S7-25, a development board based on the Xilinx Spartan-7 FPGA. It provides a great balance between size, performance, and affordability, making it an excellent choice for beginners. The board features dual Arduino headers for easy prototyping, along with PMOD headers, which allow for expansion with additional peripherals. While I am using this specific board, any Xilinx FPGA from the Spartan-7 family or higher should be compatible if you follow along with this project.
XADC (Xilinx Analog-to-Digital Converter)The XADC is an integrated Analog-to-Digital Converter (ADC) within Xilinx FPGAs, enabling them to process analog signals alongside digital logic. This feature is particularly useful for applications such as voltage monitoring, sensor data acquisition, and real-time analog signal processing. By incorporating an ADC directly into the FPGA, the need for external ADC components is reduced, simplifying hardware design and improving system integration.
Key Features of the XADC:
- Dual 12-bit ADCs capable of sampling at up to 1 MSPS (million samples per second), allowing high-speed data acquisition.
- Supports up to 16 external analog input channels (varies based on FPGA package), enabling multiple sensor or signal connections.
- On-chip sensors for FPGA temperature monitoring and power supply voltage tracking, aiding in system reliability and thermal management.
- Flexible interface options: Supports AXI Lite, Dynamic Reconfiguration Port (DRP), or Direct Register Control, allowing integration with different FPGA designs.
- Supports both unipolar (0V to 1V) and bipolar (-0.5V to 0.5V) input modes, making it adaptable to various analog signal ranges.
- Configurable alarm thresholds, enabling automatic alerts for voltage or temperature anomalies.
The XADC enhances an FPGA’s versatility by bridging the gap between analog and digital domains, making it ideal for applications requiring real-time analog signal processing within an FPGA-based system.
Vivado: Xilinx’s FPGA Design SoftwareVivado is Xilinx's comprehensive FPGA design suite, used for designing, simulating, synthesizing, and programming FPGA-based systems. It provides a complete toolchain for HDL development, including a logic analyzer, IP integrator, and debugging tools, making it essential for FPGA development. Whether you’re implementing simple logic circuits or complex digital systems, Vivado streamlines the design flow with its high-level synthesis (HLS), block-based design approach, and hardware debugging tools.
Download the Installer
- Visit the official AMD/Xilinx website and navigate to the Downloads section.
- Download the latest version of the Unified Installer, selecting the version compatible with your operating system (Windows or Linux).
- Note: You will need to sign in or create a free AMD/Xilinx account to access the downloads.
- Run the installer and follow the on-screen instructions.
- During installation, you can choose between full or custom installation, depending on whether you need additional tools like Vitis (for embedded development) or just Vivado for FPGA design.
- Be prepared for a large download (several GBs), so a stable and fast internet connection is recommended.
Once Vivado is installed, the next step is to create a new project in the AMD Vivado Design Suite. This project will serve as the workspace where you will write, simulate, and synthesize your FPGA design.
Step 1: Creating the Project
- Open Vivado and select Create New Project from the welcome screen.
- Choose a project name (e.g., DisplayXADC) and select a convenient location to save your project files.
- Click Next to proceed.
Step 2: Selecting the Project Type
- When prompted for the Project Type, choose RTL Project (Register-Transfer Level), as this allows you to write your design using SystemVerilog.
- Enable the checkbox for Do not specify sources at this time, unless you already have source files ready.
- Click Next to continue.
Step 3: Creating Source and Constraint Files
- Once inside the project, create a new source file:Select Create File → Choose SystemVerilog as the file type.Name it anything you like (e.g.,
xadc_display.sv
). - Create an empty constraint file:Select Create File → Ensure the file type is XDC (Xilinx Design Constraints).Name it anything you prefer (e.g.,
constraints.xdc
).This file will later be used to define pin mappings, clock constraints, and timing settings.
Step 4: Selecting Your FPGA Board
- A dialog box will appear, prompting you to choose between Parts and Boards.
- Select the Boards tab and type your FPGA board model (e.g., Arty S7-25).If your board doesn’t appear, click Refresh or make sure the board files are installed.
- Click Next, then Finish to complete the setup.
To use the XADC (Xilinx Analog-to-Digital Converter) in your FPGA design, you need to add and configure the XADC IP core in Vivado. Follow these steps to set it up correctly:
Step 1: Adding the XADC IP Core
- On the left side of Vivado, locate and click on IP Catalog.
- In the Search bar, type "XADC".
- Select "XADC Wizard", then double-click to open its configuration settings.
Step 2: Customizing the XADC IP
A dialog box will appear, allowing you to configure the XADC core.
- Select the Interface Type: Set the interface mode to DRP (Dynamic Reconfiguration Port). This allows real-time access to ADC conversions through registers.
- Choose the Conversion Mode: Under Mode Selection, choose "Single Channel" and set it to "Continuous Mode". This ensures that the ADC continuously samples a single analog input rather than cycling through multiple channels.
- Disable Alarms (Not Required for This Project):Navigate to the Alarms tab. Disable all alarms, as they are not needed for this application. These alarms are typically used for monitoring power supply levels and FPGA temperature.
- Selecting the Analog Input Pin:Under the Single Channel tab, locate the section for external analog inputs.Select VAUXP0/VAUXN0 for the A0 pin (or the corresponding ADC pins on your FPGA).The exact pin mapping might differ depending on your FPGA board, so check your constraints file (
.xdc
) to verify the correct pins.
Step 3: Generating the XADC IP Core
- Leave all other settings as default unless your application requires further customization.
- Click OK to close the customization window.
- In the next dialog box, select "Generate Output Products", then click "Generate".
At this point, the XADC IP core will be generated and ready for integration into your design. This module will allow your FPGA to interface with analog signals, such as reading voltage levels from a potentiometer or other sensors.
CodeConstrainFile
A constraints file (.xdc) is crucial in FPGA design as it defines the I/O pin mappings, clock constraints, and timing requirements for your project. This file tells the FPGA which physical pins to use for inputs and outputs, ensuring that your design correctly interacts with external hardware like buttons, LEDs, sensors, or communication interfaces.
Each FPGA board has a unique constraints file, as different boards have different pin assignments and hardware configurations.
Here is the repository to find constrain files for your FPGA board
Please find my constrains file attached below
Seven Segment Module
This sub module is designed to control a single seven-segment LED display at a time. It takes an input clock signal to synchronize the display updates and an input number (0–F in hexadecimal)
module sevenSegment(
input logic clk,
input logic [3:0] num,
output logic [6:0] seg_out);
always_ff @(posedge clk) begin
case(num)
4'h0: seg_out = 7'b0111111; // 0
4'h1: seg_out = 7'b0000110; // 1
4'h2: seg_out = 7'b1011011; // 2
4'h3: seg_out = 7'b1001111; // 3
4'h4: seg_out = 7'b1100110; // 4
4'h5: seg_out = 7'b1101101; // 5
4'h6: seg_out = 7'b1111101; // 6
4'h7: seg_out = 7'b0000111; // 7
4'h8: seg_out = 7'b1111111; // 8
4'h9: seg_out = 7'b1101111; // 9
4'hA: seg_out = 7'b1110111; // A
4'hB: seg_out = 7'b1111100; // B
4'hC: seg_out = 7'b0111001; // C
4'hD: seg_out = 7'b1011110; // D
4'hE: seg_out = 7'b1111001; // E
4'hF: seg_out = 7'b1110001; // F
default: seg_out = 7'b0000000; // Off state
endcase
end
endmodule
Top Module
The main module is designated as the "top" module in the hierarchy of source files. This top module serves as the entry point for the design, similar to the main
function in C programming. By structuring your design with a clear hierarchy, the top module ensures that all components work together efficiently, making debugging and extending the system easier.
Role of the Top Module
- It acts as the highest-level module that integrates and connects all other submodules in the design.
- It defines the I/O ports that interface with the FPGA’s external pins.
- It instantiates instances of other modules, passing signals between them to ensure proper communication.
Initializing Module Instances
Within the top module, you will create instances of the other modules defined in your project. These instances act like function calls in software programming, but instead of executing sequentially, they operate in parallel.
module showXADC(
input logic clk,
input logic rst,
input logic vauxp0,
input logic vauxn0,
output logic [6:0] L1,
output logic [6:0] L2
);
logic enable;
logic ready;
logic [15:0] adc_data; //stores XADC data
xadc_wiz_0 XADC_INST (
.daddr_in(6'h10), // input wire [6 : 0] daddr_in
.dclk_in(clk), // input wire dclk_in
.den_in(enable), // input wire den_in
.di_in(0), // input wire [15 : 0] di_in
.dwe_in(0), // input wire dwe_in
.reset_in(rst), // input wire reset_in
.vp_in(vp_in), // input wire vp_in
.vn_in(vn_in), // input wire vn_in
.vauxp0(vauxp0), // input wire vauxp0
.vauxn0(vauxn0), // input wire vauxn0
.busy_out(), // output wire busy_out
.channel_out(), // output wire [4 : 0] channel_out
.do_out(adc_data), // output wire [15 : 0] do_out
.drdy_out(ready), // output wire drdy_out
.eoc_out(enable), // output wire eoc_out
.eos_out(), // output wire eos_out
.alarm_out() // output wire alarm_out
);
sevenSegment display1(
.clk(clk),
.num(adc_data[11:8]), //Lower nibble LSB
.seg_out(L1)
);
sevenSegment display2(
.clk(clk),
.num(adc_data[15:12]), //Upper nibble MSB
.seg_out(L2)
);
endmodule
we begin by defining its input and output ports, which determine how the FPGA interacts with external hardware components.
- Clock input (used for synchronization)
- Analog input (for reading data from the XADC)
- Outputs for controlling the seven-segment displays
Instantiating the XADC Module
Next, we instantiate the XADC module, which continuously samples the analog input signal and converts it into a 16-bit digital value. This data is then processed to extract the relevant information for display.
Instantiating the Seven-Segment Display Modules
Since we are using two seven-segment displays, we create two instances of the sevenSegment
module. These instances receive the processed data and drive the corresponding display segments.
Data Processing and Routing
- The raw data from the XADC is 16 bits wide, but a seven-segment display typically shows only one digit at a time.
- To display meaningful values, we slice the 16-bit data, extracting only the higher-order bits, which represent the most significant digits of the ADC conversion result.
- This sliced data is then sent to the seven-segment modules, where it is converted into a binary-coded format (BCD) for display.
Once the coding phase is complete, we move on to the build and programming process, where the design is synthesized, implemented, and transferred to the FPGA hardware.
Step 1: Generating the Bitstream
- In Vivado, locate the "Build and Debug" section in the left panel.
- Click on "Generate Bitstream" to start the process.
- If prompted, click "Yes" to proceed.
- The process involves three main steps:Synthesis: Converts the SystemVerilog code into a gate-level representation.Implementation: Maps the logic onto the FPGA’s physical resources.Bitstream Generation: Creates the final binary file that will be loaded onto the FPGA.
- This process can take some time, depending on the complexity of the design and your system’s performance.
Step 2: Connecting to the FPGA Board
- Once the bitstream is successfully generated, go to "Open Hardware Manager".
- Now, connect the FPGA board to your computer using a micro USB cable.Ensure the board is powered on.
- In Hardware Manager, click on "Auto Connect".This will scan for connected FPGA devices.A list of detected hardware devices will appear.
Step 3: Programming the FPGA
- Locate your FPGA device in the list.
- Right-click on the device and select "Program Device".
- A dialog box will appear, confirming the bitstream file (
.bit
) to be used. - Click "Program" to load the design onto the FPGA.
The program is temporary in the fpga, once the power is cut it goes back to the factory or previous burnt program. If you want to burn your program into the fpga then select "Program eFUSE Registers", this will burn the program and will not be lost after power cycle.
WiringIn this project, I have used pins IO0 to IO13 to connect two seven-segment LED displays. These pins are assigned to control the individual LED segments.
Wiring the Seven-Segment Displays
- Connect the seven-segment LED segments to IO0–IO13 as per your design.
- The common ground (for a common cathode display) or common power (VCC) (for a common anode display) should be connected accordingly.
- Make sure to connect the ground of the display module to the FPGA ground (GND) to complete the circuit.
Customizing the Pin Assignments
- These pin assignments can be changed based on your project needs.
- If you modify the pin assignments, you must update the constraints file (
.xdc
) accordingly to reflect the new connections. - Verify the voltage compatibility of the seven-segment display with your FPGA's I/O voltage levels (e.g., 3.3V logic for Arty S7-25).
Connecting the Potentiometer for XADC
- You can use any generic 10K potentiometer as the analog input device.
- Connect:One end to VCC (3.3V).Other end to GND.Middle (wiper) pin to the XADC input pin (VAUXP0/VAUXN0, as defined in constraints file).
Thanks for following along with the project. Hope it helpful. Post any questions in the comments. Will be happy to help.
Comments
Please log in or sign up to comment.