Javascript required
Skip to content Skip to sidebar Skip to footer

Draw a Box in System Verilog

Welcome to Exploring FPGA Graphics. In this series, we explore graphics at the hardware level and get a feel for the power of FPGAs. We'll learn how screens piece of work, play Pong, create starfields and sprites, pigment Michelangelo'south David, simulate life, draw lines and triangles, and animate characters and shapes. Along the style, you'll experience a range of designs and techniques, from retentiveness and finite state machines to crossing clock domains and translating C algorithms into Verilog.

This postal service was completely revised in March 2022.

Updated 2022-03-xix. Become in impact with @WillFlux or open an upshot on GitHub.

Series Outline

  • Beginning FPGA Graphics (this post) - video signals and basic graphics
  • Racing the Beam - elementary demos with minimal logic
  • FPGA Pong - recreate the classic arcade on an FPGA
  • Hardware Sprites - fast, colourful graphics for games
  • Ad Astra - demo with starfields and hardware sprites
  • Framebuffers - bitmap graphics featuring Michelangelo'south David
  • Life on Screen - Conway'south Game of Life in logic
  • Lines and Triangles - drawing lines and triangles
  • 2D Shapes - filled shapes and simple pictures
  • Blithe Shapes - animation and double-buffering

Sponsor My Work
If y'all like what I do, consider sponsoring me on GitHub.
I beloved FPGAs and want to help more people detect and utilise them in their projects.
My hardware designs are open up source, and my blog is advertisement free.

Requirements

For this series, you need an FPGA board with video output. We'll be working at 640x480, so pretty much whatsoever video output volition work. It helps to exist comfortable with programming your FPGA board and reasonably familiar with Verilog.

We'll be demoing the designs with ii boards:

  • iCEBreaker (Lattice iCE40) with 12-Flake DVI Pmod
  • Digilent High-sounding A7-35T (Xilinx Artix-seven) with Pmod VGA

An increasing number of the designs are also available every bit Verilator simulations.

iCEBreaker and Arty Dev Boards

Source

The SystemVerilog designs featured in this series are available from the projf-explore git repo under the open-source MIT licence: build on them to your middle's content. The residual of the blog content is subject to standard copyright restrictions: don't republish it without permission.

SystemVerilog
We'll utilize a few choice features from SystemVerilog to make Verilog a little more pleasant. If you lot're familiar with Verilog, you'll accept no trouble. All the SystemVerilog features used are uniform with recent versions of Verilator, Yosys, and Xilinx Vivado.

Infinite and Fourth dimension

A screen is a miniature universe with its own space and time.

Seem from afar, a screen shows a smooth two-dimensional image. Upward close, it breaks up into many private blocks of colour: red, green, and blueish. We hide this complexity backside the abstract thought of a pixel: the smallest function of the screen we tin command. A typical HD screen is 1920 by 1080: two million pixels in total. Even a 640x480 display has more than 300,000 pixels.

A screen creates the illusion of movement by refreshing many times every second. At threescore Hz, a 1920x1080 screen draws 124 one thousand thousand pixels every second! The need to quickly handle then much data is a big office of the challenge of working with graphics at a hardware level.

Display connectors and cabling vary, but VGA, HDMI, and DisplayPort have a similar data design. There are three channels for colour, usually red, greenish, and bluish, and horizontal and vertical sync signals. There may too be audio and configuration data, only that'due south not important right at present.

The ruby-red, green, and blue channels behave the colour of each pixel in turn. A screen begins a new line when information technology receives a horizontal sync and a new frame on a vertical sync. The sync signals are part of blanking intervals.

Blanking intervals allow time for the electron gun in cathode ray tubes (CRTs) to move to the post-obit line (horizontal retrace) or summit of the screen (vertical retrace). Mod digital displays have retained the blanking intervals and repurposed them to transmit sound and other data.

Raster scan on a CRT Monitor

Display Timings

A screen mode is defined past its display timings. Standard timings are fix by VESA and the CTA.

In this serial, we'll use 640x480 at 60Hz. About all displays back up 640x480, and its low resource requirements make it possible to piece of work with fifty-fifty the smallest FPGAs.

Brandish timings for 640x480 at 60Hz in units of pixels:

Parameter Horizontal Vertical
Active Pixels 640 480
Front Porch 16 10
Sync Width 96 2
Back Porch 48 33
Total Blanking 160 45
Total Pixels 800 525
Sync Polarity negative negative

For other screen modes, see Video Timings: VGA, SVGA, 720p, 1080p.

The blanking interval has three parts: front porch, sync, and back porch. The front porch occurs before the sync indicate, the dorsum porch after.

If your screen showed all parts of the point, information technology would look something like this:

Display Timings Visualized

Including blanking, we have a total of 800x525 pixels.

The refresh rate is 60 Hz, so the full number of pixels per 2nd is:

800 ten 525 10 threescore = 25,200,000

Therefore, we want a pixel clock of 25.2 MHz.

Driving a Display

Having selected our brandish timings, we're ready to create a video bespeak. There are four stages:

  1. Pixel Clock
  2. Display Signals
  3. Drawing Graphics
  4. Video Output (VGA, HDMI, DisplayPort)

Driving a Display

Pixel Clock

We know we want a frequency of 25.2 MHz, only how to reach information technology?

FPGAs include phase-locked loops (PLLs) to generate custom clock frequencies. Alas, there isn't a standard mode to configure a PLL; we demand a vendor-specific design.

I have provided implementations for the Arty (Xilinx seven Series) and iCEBreaker (iCE40):

  • Arty (XC7): xc7/clock_480p.sv
  • iCEBreaker (iCE40): ice40/clock_480p.sv

NB. The iCEBreaker tin can't generate 25.ii MHz but runs fine at 25.125 MHz.

For other FPGA architectures, you'll demand to consult your vendor documentation. If you lot can't attain 25.2 MHz exactly, 25 MHz or thereabouts should be fine.

Caution: CRT Monitors
Modern displays, including multisync CRTs, should be fine with a 25.2 or 25 MHz pixel clock. Fixed-frequency CRTs, such as the original IBM 85xx series, could be damaged by an out-of-spec betoken. Use these designs at your ain take chances.

Brandish Signals

Next, we can generate sync signals from our pixel clock and brandish timings. Nosotros also want to study the electric current screen position to know when to draw things.

We do both of these things with a simple display module [simple_480p.sv]:

                                                            module                  simple_480p (                                                                              input                  wire                  logic                  clk_pix,                  // pixel clock                                                                                                                  input                  wire                  logic                  rst_pix,                  // reset in pixel clock domain                                                                                                                  output                  logic                  [9                  :                  0] sx,                  // horizontal screen position                                                                                                                  output                  logic                  [9                  :                  0] sy,                  // vertical screen position                                                                                                                  output                  logic                  hsync,                  // horizontal sync                                                                                                                  output                  logic                  vsync,                  // vertical sync                                                                                                                  output                  logic                  de                  // data enable (depression in blanking interval)                                                                                                                  );                                                                                                                                          // horizontal timings                                                                                                                  parameter                  HA_END                  =                  639;                  // end of active pixels                                                                                                                  parameter                  HS_STA                  =                  HA_END                  +                  16;                  // sync starts later front porch                                                                                                                  parameter                  HS_END                  =                  HS_STA                  +                  96;                  // sync ends                                                                                                                  parameter                  LINE                  =                  799;                  // last pixel on line (after back porch)                                                                                                                                                                              // vertical timings                                                                                                                  parameter                  VA_END                  =                  479;                  // cease of active pixels                                                                                                                  parameter                  VS_STA                  =                  VA_END                  +                  10;                  // sync starts after front porch                                                                                                                  parameter                  VS_END                  =                  VS_STA                  +                  2;                  // sync ends                                                                                                                  parameter                  SCREEN                  =                  524;                  // last line on screen (after back porch)                                                                                                                                                                              always_comb                  begin                                                                              hsync                  =                  ~(sx                  >=                  HS_STA                  &&                  sx                  <                  HS_END);                  // capsize: negative polarity                                                                                                                  vsync                  =                  ~(sy                  >=                  VS_STA                  &&                  sy                  <                  VS_END);                  // invert: negative polarity                                                                                                                  de                  =                  (sx                  <=                  HA_END                  &&                  sy                  <=                  VA_END);                                                                              end                                                                                                                                          // summate horizontal and vertical screen position                                                                                                                  always_ff                  @(posedge                  clk_pix)                  begin                                                                              if                  (sx                  ==                  LINE)                  begin                  // last pixel on line?                                                                                                                  sx                  <=                  0;                                                                              sy                  <=                  (sy                  ==                  SCREEN)                  ?                  0                  :                  sy                  +                  1;                  // last line on screen?                                                                                                                  end                  else                  begin                                                                              sx                  <=                  sx                  +                  1;                                                                              terminate                                                                              if                  (rst_pix)                  begin                                                                              sx                  <=                  0;                                                                              sy                  <=                  0;                                                                              finish                                                                              end                                                                              endmodule                                                    

ProTip: The last assignment wins in Verilog, and so the reset overrides existing values for sx and sy.

sx and sy shop the horizontal and vertical screen position. Counting starts at zero, so the maximum values are 799 for sx and 524 for sy, requiring 10 bits to concord the coordinates (iix = 1024).

For simplicity, we put blanking afterwards the visible pixels; that way, (0,0) is the summit-left visible pixel and (639,479) the bottom correct.

de is data enable, which is depression during the blanking interval and tells united states when it's rubber to describe.

From the display timings, nosotros know our sync polarity is negative for both hsync and vsync. Negative polarity ways that a low voltage indicates a sync.

The following simulation shows the vertical sync starting at line 489. The vertical sync is depression for two lines, every bit expected from the brandish timings. Note the horizontal sync at the terminate of each line.

Sync Signal Simulation

Test Benches

If you're using Vivado, try exercising the designs with these test benches:

  • Clock Examination Demote
  • Simple Display Examination Demote

Some things to check:

  • What is the pixel clock menses?
  • How long does the pixel clock accept to lock?
  • Does a frame final exactly 1/60th of a second?
  • How much fourth dimension does a unmarried line last?
  • What is the maximum values of sx and sy when de is low?

You tin find instructions for running the Vivado simulations in the source README.

Drawing Graphics

For our showtime design, we're going to depict a square like this:

Square

Nosotros use the screen coordinates (sx,sy) to ascertain a foursquare in the center of the screen:

                                                            logic                  square;                                                                              always_comb                  begin                                                                              square                  =                  (sx                  >                  220                  &&                  sx                  <                  420)                  &&                  (sy                  >                  140                  &&                  sy                  <                  340);                                                                              stop                                                    

12-bit Colour

The VGA and DVI Pmods output 12-chip colour with three 4-bit channels: scarlet, green, and blue.

Nosotros can correspond a specific colour using a hex triplet:

  • #F00 - brilliant red
  • #FA0 - orange
  • #0E3 - brilliant dark-green
  • #137 - nighttime blue
  • #FFF - white

In Verilog, hex literals utilise the letter h, so nosotros tin gear up our colours as follows:

                                                            logic                  [iii                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  brainstorm                                                                              paint_r                  =                  (foursquare)                  ?                  4'hF                  :                  4'h1;                                                                              paint_g                  =                  (foursquare)                  ?                  4'hF                  :                  4'h3;                                                                              paint_b                  =                  (square)                  ?                  4'hF                  :                  iv'h7;                                                                              stop                                                    

We generate a dissever bespeak for each color aqueduct, fix for video output.

Video Output

Video output works differently for each board and simulation, and so we'll encompass them in turn.

Arty VGA

VGA output is straightforward. Nosotros register each bespeak to meliorate timing and avoid skew:

                                                            // VGA Pmod output                                                                                                                  always_ff                  @(posedge                  clk_pix)                  begin                                                                              vga_hsync                  <=                  hsync;                                                                              vga_vsync                  <=                  vsync;                                                                              if                  (de)                  begin                                                                              vga_r                  <=                  paint_r;                                                                              vga_g                  <=                  paint_g;                                                                              vga_b                  <=                  paint_b;                                                                              end                  else                  begin                  // VGA colour should exist black in blanking interval                                                                                                                  vga_r                  <=                  4'h0;                                                                              vga_g                  <=                  4'h0;                                                                              vga_b                  <=                  four'h0;                                                                              end                                                                              stop                                                    

The VGA Pmod handles the conversion of digital color signals into counterpart voltages. Yous need to ensure the colour is black during blanking; otherwise, yous'll see corrupted output on some screens.

iCEBreaker DVI

The TFP410 chip on the DVI Pmod takes our colour and sync signals and encodes them into DVI using Transition-minimized differential signalling (TMDS).

We utilise the SB_IO primitive to produce high-quality output from the iCE40 FPGA. It'due south not necessary to understand how SB_IO works for this serial; apply this snippet in your designs, and all will be well:

                                                            // DVI Pmod output                                                                                                SB_IO #(                                                                              .PIN_TYPE(half dozen                  'b010100)                  // PIN_OUTPUT_REGISTERED                                                                                                ) dvi_signal_io [14                  :                  0] (                                                                              .PACKAGE_PIN({dvi_hsync, dvi_vsync, dvi_de, dvi_r, dvi_g, dvi_b}),                                                                              .OUTPUT_CLK(clk_pix),                                                                              .D_OUT_0({hsync, vsync, de, paint_r, paint_g, paint_b}),                                                                              .D_OUT_1()                                                            );                                                                                                                                          // DVI Pmod clock output: 180° out of phase with other DVI signals                                                                                                SB_IO #(                                                                              .PIN_TYPE(vi                  'b010000)                  // PIN_OUTPUT_DDR                                                                                                ) dvi_clk_io (                                                                              .PACKAGE_PIN(dvi_clk),                                                                              .OUTPUT_CLK(clk_pix),                                                                              .D_OUT_0(1                  'b0),                                                                              .D_OUT_1(1                  'b1)                                                            );                                                    

Lattice SB_IO
The SB_IO archaic (with registered outputs) ensures our DVI signals are in sync when they get out the FPGA. The DVI clock is 180 degrees out of phase and then that the TFP410 will sample the eye of the colour values. Yous can learn more about iCE primitives from the Lattice ICE Engineering science Library.

Verilator Sim

The simulation output is like to the High-sounding VGA, simply it expects viii bits per colour channel:

                                                            // SDL output (viii $.25 per colour channel)                                                                                                                  always_ff                  @(posedge                  clk_pix)                  begin                                                                              sdl_sx                  <=                  sx;                                                                              sdl_sy                  <=                  sy;                                                                              sdl_de                  <=                  de;                                                                              sdl_r                  <=                  {2{paint_r}};                  // double signal width from 4 to viii bits                                                                                                                  sdl_g                  <=                  {ii{paint_g}};                                                                              sdl_b                  <=                  {two{paint_b}};                                                                              finish                                                    

Square Ane

Bringing the 4 stages together nosotros accept a consummate top module:

  • Arty (XC7): xc7/top_square.sv
  • iCEBreaker (iCE40): ice40/top_square.sv
  • Verilator Sim: sim/top_square.sv

All of these top modules are listed in full, below. Encounter if you can match each of the four stages of driving a display with the Verilog for your peak module.

High-sounding VGA Square

                                                            module                  top_square (                                                                              input                  wire                  logic                  clk_100m,                  // 100 MHz clock                                                                                                                  input                  wire                  logic                  btn_rst_n,                  // reset button                                                                                                                  output                  logic                  vga_hsync,                  // VGA horizontal sync                                                                                                                  output                  logic                  vga_vsync,                  // VGA vertical sync                                                                                                                  output                  logic                  [three                  :                  0] vga_r,                  // 4-flake VGA red                                                                                                                  output                  logic                  [3                  :                  0] vga_g,                  // iv-bit VGA green                                                                                                                  output                  logic                  [3                  :                  0] vga_b                  // iv-bit VGA blue                                                                                                                  );                                                                                                                                          // generate pixel clock                                                                                                                  logic                  clk_pix;                                                                              logic                  clk_pix_locked;                                                                              clock_480p clock_pix_inst (                                                                              .clk_100m,                                                                              .rst(!btn_rst_n),                  // reset push is active low                                                                                                                  .clk_pix,                                                                              .clk_pix_5x(),                  // not used for VGA output                                                                                                                  .clk_pix_locked                                                                              );                                                                                                                                          // display sync signals and coordinates                                                                                                                  localparam                  CORDW                  =                  x;                  // screen coordinate width in bits                                                                                                                  logic                  [CORDW-                  one                  :                  0] sx, sy;                                                                              logic                  hsync, vsync, de;                                                                              simple_480p display_inst (                                                                              .clk_pix,                                                                              .rst_pix(!clk_pix_locked),                  // wait for clock lock                                                                                                                  .sx,                                                                              .sy,                                                                              .hsync,                                                                              .vsync,                                                                              .de                                                                              );                                                                                                                                          // define a square with screen coordinates                                                                                                                  logic                  square;                                                                              always_comb                  begin                                                                              square                  =                  (sx                  >                  220                  &&                  sx                  <                  420)                  &&                  (sy                  >                  140                  &&                  sy                  <                  340);                                                                              end                                                                                                                                          // pigment colours: white inside foursquare, blue outside                                                                                                                  logic                  [3                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  begin                                                                              paint_r                  =                  (foursquare)                  ?                  4'hF                  :                  iv'h1;                                                                              paint_g                  =                  (square)                  ?                  iv'hF                  :                  4'h3;                                                                              paint_b                  =                  (foursquare)                  ?                  4'hF                  :                  iv'h7;                                                                              finish                                                                                                                                          // VGA Pmod output                                                                                                                  always_ff                  @(posedge                  clk_pix)                  begin                                                                              vga_hsync                  <=                  hsync;                                                                              vga_vsync                  <=                  vsync;                                                                              if                  (de)                  begin                                                                              vga_r                  <=                  paint_r;                                                                              vga_g                  <=                  paint_g;                                                                              vga_b                  <=                  paint_b;                                                                              end                  else                  brainstorm                  // VGA colour should be black in blanking interval                                                                                                                  vga_r                  <=                  4'h0;                                                                              vga_g                  <=                  four'h0;                                                                              vga_b                  <=                  4'h0;                                                                              finish                                                                              end                                                                              endmodule                                                    

iCEBreaker DVI Square

                                                            module                  top_square (                                                                              input                  wire                  logic                  clk_12m,                  // 12 MHz clock                                                                                                                  input                  wire                  logic                  btn_rst,                  // reset button                                                                                                                  output                  logic                  dvi_clk,                  // DVI pixel clock                                                                                                                  output                  logic                  dvi_hsync,                  // DVI horizontal sync                                                                                                                  output                  logic                  dvi_vsync,                  // DVI vertical sync                                                                                                                  output                  logic                  dvi_de,                  // DVI data enable                                                                                                                  output                  logic                  [three                  :                  0] dvi_r,                  // 4-bit DVI crimson                                                                                                                  output                  logic                  [3                  :                  0] dvi_g,                  // 4-fleck DVI green                                                                                                                  output                  logic                  [iii                  :                  0] dvi_b                  // 4-bit DVI blueish                                                                                                                  );                                                                                                                                          // generate pixel clock                                                                                                                  logic                  clk_pix;                                                                              logic                  clk_pix_locked;                                                                              clock_480p clock_pix_inst (                                                                              .clk_12m,                                                                              .rst(btn_rst),                                                                              .clk_pix,                                                                              .clk_pix_locked                                                                              );                                                                                                                                          // display sync signals and coordinates                                                                                                                  localparam                  CORDW                  =                  ten;                  // screen coordinate width in $.25                                                                                                                  logic                  [CORDW-                  1                  :                  0] sx, sy;                                                                              logic                  hsync, vsync, de;                                                                              simple_480p display_inst (                                                                              .clk_pix,                                                                              .rst_pix(!clk_pix_locked),                  // look for clock lock                                                                                                                  .sx,                                                                              .sy,                                                                              .hsync,                                                                              .vsync,                                                                              .de                                                                              );                                                                                                                                          // ascertain a square with screen coordinates                                                                                                                  logic                  square;                                                                              always_comb                  begin                                                                              square                  =                  (sx                  >                  220                  &&                  sx                  <                  420)                  &&                  (sy                  >                  140                  &&                  sy                  <                  340);                                                                              terminate                                                                                                                                          // paint colours: white inside foursquare, bluish outside                                                                                                                  logic                  [3                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  begin                                                                              paint_r                  =                  (square)                  ?                  4'hF                  :                  4'h1;                                                                              paint_g                  =                  (foursquare)                  ?                  iv'hF                  :                  4'h3;                                                                              paint_b                  =                  (foursquare)                  ?                  four'hF                  :                  4'h7;                                                                              terminate                                                                                                                                          // DVI Pmod output                                                                                                                  SB_IO #(                                                                              .PIN_TYPE(6                  'b010100)                  // PIN_OUTPUT_REGISTERED                                                                                                                  ) dvi_signal_io [14                  :                  0] (                                                                              .PACKAGE_PIN({dvi_hsync, dvi_vsync, dvi_de, dvi_r, dvi_g, dvi_b}),                                                                              .OUTPUT_CLK(clk_pix),                                                                              .D_OUT_0({hsync, vsync, de, paint_r, paint_g, paint_b}),                                                                              .D_OUT_1()                                                                              );                                                                                                                                          // DVI Pmod clock output: 180° out of phase with other DVI signals                                                                                                                  SB_IO #(                                                                              .PIN_TYPE(half-dozen                  'b010000)                  // PIN_OUTPUT_DDR                                                                                                                  ) dvi_clk_io (                                                                              .PACKAGE_PIN(dvi_clk),                                                                              .OUTPUT_CLK(clk_pix),                                                                              .D_OUT_0(1                  'b0),                                                                              .D_OUT_1(1                  'b1)                                                                              );                                                                              endmodule                                                    

Verilator Sim

The Verilator simulation works a picayune differently; we output the coordinates sdl_sx, and sdl_sy, as well as the colour data.

                                                            module                  top_square #(parameter                  CORDW=                  10) (                  // coordinate width                                                                                                                  input                  wire                  logic                  clk_pix,                  // pixel clock                                                                                                                  input                  wire                  logic                  sim_rst,                  // sim reset                                                                                                                  output                  logic                  [CORDW-                  i                  :                  0] sdl_sx,                  // horizontal SDL position                                                                                                                  output                  logic                  [CORDW-                  1                  :                  0] sdl_sy,                  // vertical SDL position                                                                                                                  output                  logic                  sdl_de,                  // data enable (low in blanking interval)                                                                                                                  output                  logic                  [7                  :                  0] sdl_r,                  // viii-bit crimson                                                                                                                  output                  logic                  [7                  :                  0] sdl_g,                  // 8-flake green                                                                                                                  output                  logic                  [7                  :                  0] sdl_b                  // 8-chip bluish                                                                                                                  );                                                                                                                                          // display sync signals and coordinates                                                                                                                  logic                  [CORDW-                  1                  :                  0] sx, sy;                                                                              logic                  de;                                                                              simple_480p display_inst (                                                                              .clk_pix,                                                                              .rst_pix(sim_rst),                                                                              .sx,                                                                              .sy,                                                                              .hsync(),                                                                              .vsync(),                                                                              .de                                                                              );                                                                                                                                          // define a square with screen coordinates                                                                                                                  logic                  square;                                                                              always_comb                  begin                                                                              square                  =                  (sx                  >                  220                  &&                  sx                  <                  420)                  &&                  (sy                  >                  140                  &&                  sy                  <                  340);                                                                              end                                                                                                                                          // paint colours: white inside square, blueish outside                                                                                                                  logic                  [three                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  begin                                                                              paint_r                  =                  (foursquare)                  ?                  4'hF                  :                  4'h1;                                                                              paint_g                  =                  (foursquare)                  ?                  4'hF                  :                  4'h3;                                                                              paint_b                  =                  (square)                  ?                  4'hF                  :                  four'h7;                                                                              end                                                                                                                                          // SDL output (8 bits per colour aqueduct)                                                                                                                  always_ff                  @(posedge                  clk_pix)                  begin                                                                              sdl_sx                  <=                  sx;                                                                              sdl_sy                  <=                  sy;                                                                              sdl_de                  <=                  de;                                                                              sdl_r                  <=                  {2{paint_r}};                  // double betoken width from four to 8 bits                                                                                                                  sdl_g                  <=                  {two{paint_g}};                                                                              sdl_b                  <=                  {2{paint_b}};                                                                              cease                                                                              endmodule                                                    

NB. The Verilator simulation receives its pixel clock from the C++ wrapper, so there'south no pixel clock generation in this version.

Constraints

Before building the design, we need lath constraints. The constraints map the pins on the FPGA to the signals in our design. For example, nosotros need to know which FPGA pin connects to the reset button and which to the vertical sync.

Take a await at the constraints for your board:

  • Arty Constraints: high-sounding.xdc
  • iCEBreaker Constraints: icebreaker.pcf

The Verilator sim doesn't require constraints.

Building

Each part of this series includes a README with build instructions. I have provided bones instructions hither to become you started. If you need aid with your board or tools, I recommend the Digilent Forum for Arty and 1BitSquared Discord for iCEBreaker.

Arty

We build Arty designs using Xilinx Vivado. To create a Vivado project, clone the projf-explore repo from GitHub. Then, start Vivado and run the following in the Tcl Console:

                                          cd projf-explore/graphics/fpga-graphics/xc7/vivado                                                            source ./create_project.tcl                                                    

This creates a Vivado project with all four designs from this post.

iCEBreaker

We build iCEBreaker designs with the open up-source toolchain of Yosys, nextpnr, and IceStorm Tools. If yous don't already accept these tools run across the README.

To build and plan the square design; clone the projf-explore repo, then in a crush:

                                          cd projf-explore/graphics/fpga-graphics/ice40                                                            make square                                                            iceprog square.bin                                                    

If yous have problems edifice the iCE40 designs, make sure you're using Yosys 0.ten or later on.

Verilator Simulation

For details on installing Verilator and building the design, encounter Verilog Simulation with Verilator and SDL and the Simulation README.

Flags

Our first design is not but a foursquare only also the naval signal flag for the letter 'P' (bluish Peter).

I have created designs for two more flags: Ethiopia and Sweden. Take a expect at these examples, then accept a go at cartoon a flag yourself.

Flag of Federal democratic republic of ethiopia

The traditional flag of Ethiopia is a tricolour of greenish, yellow, and red.

Traditional Flag of Ethiopia

We simply need the horizontal screen coordinate, sy, to define this flag:

                                                            logic                  [3                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  brainstorm                                                                              if                  (sy                  <                  160)                  begin                  // top of flag is green                                                                                                                  paint_r                  =                  4'h0;                                                                              paint_g                  =                  4'h9;                                                                              paint_b                  =                  iv'h3;                                                                              end                  else                  if                  (sy                  <                  320)                  begin                  // middle of flag is yellowish                                                                                                                  paint_r                  =                  4'hF;                                                                              paint_g                  =                  four'hE;                                                                              paint_b                  =                  4'h1;                                                                              end                  else                  brainstorm                  // bottom of flag is red                                                                                                                  paint_r                  =                  4'hE;                                                                              paint_g                  =                  4'h1;                                                                              paint_b                  =                  4'h2;                                                                              end                                                                              stop                                                    

You can find the total flag design in git:

  • Arty (XC7): xc7/top_flag_ethiopia.sv
  • iCEBreaker (iCE40): ice40/top_flag_ethiopia.sv
  • Verilator Sim: sim/top_flag_ethiopia.sv

Flag of Sweden

The flag of Sweden consists of a yellow Nordic cross on a bluish background.

Flag of Sweden

The official flag has a ratio of eight:5, which equates to 640x400 on our screen:

                                                            logic                  [three                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  begin                                                                              if                  (sy                  >=                  400)                  brainstorm                  // black outside the flag area                                                                                                                  paint_r                  =                  4'h0;                                                                              paint_g                  =                  four'h0;                                                                              paint_b                  =                  iv'h0;                                                                              cease                  else                  if                  (sy                  >                  160                  &&                  sy                  <                  240)                  begin                  // yellow cross horizontal                                                                                                                  paint_r                  =                  4'hF;                                                                              paint_g                  =                  iv'hC;                                                                              paint_b                  =                  4'h0;                                                                              end                  else                  if                  (sx                  >                  200                  &&                  sx                  <                  280)                  brainstorm                  // yellowish cross vertical                                                                                                                  paint_r                  =                  4'hF;                                                                              paint_g                  =                  4'hC;                                                                              paint_b                  =                  4'h0;                                                                              end                  else                  begin                  // blue flag background                                                                                                                  paint_r                  =                  iv'h0;                                                                              paint_g                  =                  4'h6;                                                                              paint_b                  =                  4'hA;                                                                              end                                                                              cease                                                    

You can discover the full flag design in git:

  • Arty (XC7): xc7/top_flag_sweden.sv
  • iCEBreaker (iCE40): ice40/top_flag_sweden.sv
  • Verilator Sim: sim/top_flag_sweden.sv

Colour Examination

No introduction to graphics hardware would exist complete without a colour gradient. 12-bit graphics can handle 4,096 colours; this demo shows 256 of them.

Colour Gradient Test

For this example, I've kept the blueish level contant while varying the red and green:

                                                            logic                  [3                  :                  0] paint_r, paint_g, paint_b;                                                                              always_comb                  begin                                                                              if                  (sx                  <                  256                  &&                  sy                  <                  256)                  begin                  // color square in top-left 256x256 pixels                                                                                                                  paint_r                  =                  sx[7                  :                  4];                  // 16 horizontal pixels of each cerise level                                                                                                                  paint_g                  =                  sy[seven                  :                  4];                  // 16 vertical pixels of each green level                                                                                                                  paint_b                  =                  4'h4;                  // abiding blue level                                                                                                                  cease                  else                  begin                  // otherwise black                                                                                                                  paint_r                  =                  4'h0;                                                                              paint_g                  =                  4'h0;                                                                              paint_b                  =                  4'h0;                                                                              end                                                                              end                                                    

We select bits [7:4] from sx and sy, so the color level changes every 16 pixels.

You can find the full pattern in git:

  • Arty (XC7): xc7/top_colour.sv
  • iCEBreaker (iCE40): ice40/top_colour.sv
  • Verilator Sim: sim/top_colour.sv

Next Time

I hope y'all enjoyed this introduction to FPGA Graphics. Next time, we'll be Racing the Beam to create simple demos with our new graphics skills. Cheque out the site map for more fun FPGA posts.

What'due south Possible?

Hither are some projects to inspire you:

  • Driving a 32×32 RGB LED Matrix past Glen Akins
  • icestation-32: open-source FPGA game console by Dan Rodrigues
  • VGA Clock by Matt Venn
  • Racing the Beam Ray Tracer by Tom Verbeure
  • FPGA Media Player by ultraembedded

Constructive feedback is always welcome. Go far bear on with @WillFlux or open an effect on GitHub.

espieroustich.blogspot.com

Source: https://projectf.io/posts/fpga-graphics/