top of page
Adiuvo Engineering & Training logo
MicroZed Chronicles icon

Building our First Agilex 3 Application

On our journey of Agilex 3 exploration to date we have provided an overview of the Agilex 3 family and examined key aspects we need to understand to design in an Agilex 3 FPGA as a chip-down design.


Now it is time to start looking at how we develop applications for the Agilex 3 on the Terasic A3 Nano.


Let's start with a look at the toolchain. For this development we will be using Quartus Prime 25.1. Within Quartus Prime we will find not only the synthesis and implementation tools but also several other tools which significantly aid development, including:


Platform Designer – System integration tool which enables system integration based around both Altera and Custom IP.


Signal Tap Logic Analyzer – In-chip logic debugging which enables analysis of the design at run-time.


Questa Altera FPGA Edition – A version of the popular Questa Simulator specifically optimised for Altera devices.


To get started creating an application, the first step is to download and install Quartus 25.1 from the Altera web site. The installation tool enables us to customise exactly the installation that we desire on our system.


For this installation I will be installing Quartus Prime Pro, Agilex 3, Agilex 5 and Cyclone 10 GX devices along with the Power and Thermal Calculator and Quartus programmer. I will not be installing the Questa simulator as I already have a separate full license for Questa. On my machine this installed in a little under an hour including the download.

ree

With Quartus installed I wanted to get started on a simple design. I like visual demonstrations so instead of blinking a LED I decided that I would write a VGA test pattern and output it over the TMDS ports using a Pmod VGA. I know the board has an HDMI and that is something I want to explore in a later blog, but for now, I wanted a simple solution to pipe-clean the process.


You can write a simple VGA test pattern with a couple of counters and counter decoding for the test pattern. I opted for an 800 pixel by 600 line image at 72Hz as this uses a 50 MHz pixel clock. It just happens the clocks on the A3 Nano are 50 MHz which makes such a solution nice and simple.


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- 50.000 MHz pixel clock in, VGA 800x600@72 out (12-bit RGB + HS/VS)
entity vga_test_800x600_50mhz is
  port (
    i_clk_50 : in  std_logic;                          -- 50.000 MHz pixel clock
    --i_resetn : in  std_logic;                          -- active-low reset
    o_vga_r  : out std_logic_vector(3 downto 0);
    o_vga_g  : out std_logic_vector(3 downto 0);
    o_vga_b  : out std_logic_vector(3 downto 0);
    o_vga_hs : out std_logic;                          -- HS positive
    o_vga_vs : out std_logic                           -- VS positive
  );
end entity;

architecture rtl of vga_test_800x600_50mhz is

  ------------------------------------------------------------------------
  -- VESA SVGA 800x600 @ 72 Hz timing @ 50.000 MHz pixel clock 
  -- H: 800 active, 56 fp, 120 sync, 64 bp -> 1040 total (fH ≈ 48.077 kHz)
  -- V: 600 active, 37 fp, 6 sync, 23 bp   -> 666 total   (fV ≈ 72.189 Hz)
  ------------------------------------------------------------------------
  constant c_H_ACTIVE   : integer := 800;
  constant c_H_FP       : integer := 56;
  constant c_H_SYNC     : integer := 120;
  constant c_H_BP       : integer := 64;
  constant c_H_TOTAL    : integer := c_H_ACTIVE + c_H_FP + c_H_SYNC + c_H_BP;  -- 1040

  constant c_V_ACTIVE   : integer := 600;
  constant c_V_FP       : integer := 37;
  constant c_V_SYNC     : integer := 6;
  constant c_V_BP       : integer := 23;
  constant c_V_TOTAL    : integer := c_V_ACTIVE + c_V_FP + c_V_SYNC + c_V_BP;  -- 666

  constant c_HS_POL_POS : boolean := true;
  constant c_VS_POL_POS : boolean := true;

  -- Counters (0..1039) and (0..665)
  signal s_h_cnt : unsigned(10 downto 0) := (others => '0');  -- 11 bits
  signal s_v_cnt : unsigned(9  downto 0) := (others => '0');  -- 10 bits

  signal s_active_video : std_logic;

  -- Syncs
  signal s_hs, s_vs : std_logic;

  -- Colors (4:4:4)
  signal s_r, s_g, s_b : std_logic_vector(3 downto 0);
begin
  ------------------------------------------------------------------------
  -- H/V counters
  ------------------------------------------------------------------------
  p_counters : process(i_clk_50)
  begin
    if rising_edge(i_clk_50) then
        if s_h_cnt = to_unsigned(c_H_TOTAL-1, s_h_cnt'length) then
          s_h_cnt <= (others => '0');
          if s_v_cnt = to_unsigned(c_V_TOTAL-1, s_v_cnt'length) then
            s_v_cnt <= (others => '0');
          else
            s_v_cnt <= s_v_cnt + 1;
          end if;
        else
          s_h_cnt <= s_h_cnt + 1;
        end if;
    end if;
  end process;

s_active_video <= '1' when (s_h_cnt < to_unsigned(c_H_ACTIVE, s_h_cnt'length) and s_v_cnt < to_unsigned(c_V_ACTIVE, s_v_cnt'length))
else '0';

  ------------------------------------------------------------------------
  -- Sync generation (registered)
  ------------------------------------------------------------------------
  p_syncs : process(i_clk_50)
    variable v_h : integer;
    variable v_v : integer;
    variable v_hs_pulse, v_vs_pulse : std_logic;
  begin
    if rising_edge(i_clk_50) then
      v_h := to_integer(s_h_cnt);
      v_v := to_integer(s_v_cnt);

      v_hs_pulse := '1' when (v_h >= c_H_ACTIVE + c_H_FP) and
                         (v_h <  c_H_ACTIVE + c_H_FP + c_H_SYNC) else '0';
      v_vs_pulse := '1' when (v_v >= c_V_ACTIVE + c_V_FP) and
                         (v_v <  c_V_ACTIVE + c_V_FP + c_V_SYNC) else '0';

      s_hs <= v_hs_pulse when c_HS_POL_POS else not v_hs_pulse;
      s_vs <= v_vs_pulse when c_VS_POL_POS else not v_vs_pulse;
    end if;
  end process;

  o_vga_hs <= s_hs;
  o_vga_vs <= s_vs;

  ------------------------------------------------------------------------
  -- Test pattern (12-bit RGB)
  --  Top    0..199: Red gradient across X
  --  Middle 200..399: 8 vertical color bars (100 px each)
  --  Bottom 400..599: 32×32 checkerboard (bit toggles)
  ------------------------------------------------------------------------
  p_pattern : process(i_clk_50)
    variable v_x, v_y : integer;
  begin
    if rising_edge(i_clk_50) then
      if s_active_video = '1' then
        v_x := to_integer(s_h_cnt);
        v_y := to_integer(s_v_cnt);

        if v_y < 200 then
          -- Red gradient using s_h_cnt[9:6] -> 16 steps across width
          s_r <= std_logic_vector(s_h_cnt(9 downto 6));
          s_g <= (others => '0');
          s_b <= (others => '0');

        elsif v_y < 400 then
          -- 8 vertical color bars (100 px each across 800)
          if    v_x < 100 then  s_r <= x"F"; s_g <= x"F"; s_b <= x"F"; 
          elsif v_x < 200 then  s_r <= x"F"; s_g <= x"F"; s_b <= x"0";
          elsif v_x < 300 then  s_r <= x"0"; s_g <= x"F"; s_b <= x"F"; 
          elsif v_x < 400 then  s_r <= x"0"; s_g <= x"F"; s_b <= x"0"; 
          elsif v_x < 500 then  s_r <= x"F"; s_g <= x"0"; s_b <= x"F";
          elsif v_x < 600 then  s_r <= x"F"; s_g <= x"0"; s_b <= x"0"; 
          elsif v_x < 700 then  s_r <= x"0"; s_g <= x"0"; s_b <= x"F"; 
          else                  s_r <= x"0"; s_g <= x"0"; s_b <= x"0"; 
          end if;

        else
          -- Checkerboard: toggle every 32 px using bit 5 of counters
          if (s_h_cnt(5) xor s_v_cnt(5)) = '0' then
            s_r <= x"8"; s_g <= x"8"; s_b <= x"8";     -- light gray
          else
            s_r <= x"2"; s_g <= x"2"; s_b <= x"2";     -- dark gray
          end if;
        end if;

        -- Optional 1-pixel white border
        if (v_x = 0) or (v_x = c_H_ACTIVE-1) or (v_y = 0) or 
		(v_y = c_V_ACTIVE-1) then
          	s_r <= x"F"; s_g <= x"F"; s_b <= x"F";
        end if;
      else
        s_r <= (others => '0');
        s_g <= (others => '0');
        s_b <= (others => '0');
      end if;
    end if;
  end process;

  o_vga_r <= s_r;
  o_vga_g <= s_g;
  o_vga_b <= s_b;

end architecture;

With the code written, the next step is to open Quartus 25.1 and create a new project. Opening Quartus for the first time will walk you through the licensing process to obtain the free Agilex 3 License.

ree
ree
ree

With a license available, Quartus will open and we are able to get started with our first project. The first element of the new project creation wizard allows us to enter a project location and name for the project to be created.

ree

The next step is to select the component to be used for the project. On the A3 Nano board we have the A3CZ135BB18AE7S device.

ree

The final step of the project creation process is to add in any source code files we have created, e.g., our VGA RTL file. Leave the tool settings unchanged and you will see a summary of the project to be created. Click finish.

ree

As we have a project created with an RTL file, we are then able to compile the design and open the pin planner to assign the I/O to the pins on the FPGA. In this case it is pretty straightforward. We are able to assign the pin locations and I/O standards.

ree

The final thing we need to do is define the constraints for the FPGA design. For this one it is straightforward; we only have one clock at 50MHz. We can create an SDC file and add in the constraints necessary.

create_clock -name sys_clk -period 20.000 [get_ports {i_clk_50}]

We are now able to implement the design and generate the programming file.


ree

Once the design has completed its implementation, we can open Timing Analyzer and ensure there is timing closure. This is a pretty simple design so we should be able to close timing.

ree

All that remains now is to try the design on the actual board and with a VGA display connected.


Running this shows the resultant image below.

ree
ree

We can see this simple design and project flow worked as intended. In our next blog we are going to explore the integration of the HDMI/DVI display and examine how we can look and debug in our FPGA so that we understand the flow when we get to more complex projects.


UK FPGA Conference


FPGA Horizons - October 7th 2025 - THE FPGA Conference, find out more here.


Sponsored by Altera


bottom of page