
Hello!
In
a previous article, my colleague
Des333 implemented a framebuffer for an LCD that runs on an
ILI9341 graphics controller. However, writing it required substantial experience in developing the RTL code.
In addition, not everyone has an embedded LCD display at hand, but for sure there is a monitor with a VGA input. What can you do if there is not enough development experience for FPGA, but there is SoC, but do you want to do something interesting?
In this article we will describe how to develop a graphics controller, having a board with SoC (Altera Cyclone V), a display with VGA and minimal knowledge of HDL languages ​​(in our case, Verilog).
')
For example, we will use our scarves, but everything described will work on others.
Who cares, I ask under the cat.
I plan to tell in the following order:
- First, a little about the interaction architecture
- Briefly about connecting to VGA
- How to get the firmware using only Quartus Qsys
- How to explain to the kernel that there is a graphics controller. I'll tell you what to add to dtb and build drivers
- How to get the terminal and X on the display
I will use the
CB-CV-SOM debug card that works in conjunction with the
CV-SE-SOM SoDIMM module:

We have a shield for this debug board, in addition to VGA there are many interesting things (see
metrotek.spb.ru/cbcvsom.html )
Architecture
To display the image on the display, we need a
framebuffer , a driver and a scanning module, which will provide a link between the processor and the display, as well as provide a continuous frame update.
In SoC, the ARM (also called HPS - Hard Processing System) is connected to DDR3 memory (1 GB in our case), and it will contain our framebuffer. And in the FPGA there will be a module that we will need to do with Qsys.
Here is the diagram:

Everything will work like this:
- The driver configures the module in the FPGA: after that, the module is ready to receive data from the SDRAM controller and “deploy” it to the display
- In / dev / fb0 write the picture. Data gets into framebuffer in DDR
- A module in the FPGA continuously reads the framebuffer and updates the screen.
How does VGA work
VGA (Video Graphics Array) is a video interface that uses an analog signal to transmit color information. The format of the signals and their behavior are similar to the television signal.
List of signals:
vga_vs_o - vertical sync
vga_hs_o - horizontal sync
vga_r_o - data of the red pixel component
vga_g_o - data of the green pixel component
vga_b_o - data is the blue component of the pixel
Shield supports 16 bits per color, which means that 5 bits are allocated to blue and red, and 6 bits to green. DAC is made according to the R2R scheme.
Monitors have different resolutions and image refresh rates. These parameters are adjusted in VGA using vertical and horizontal synchronization. Which have the following parameters:
- The clock frequency of the appearance of new pixels.
- Front porch - sync pulse quenching time.
- Back porch is the rise time of the sync pulse.
- Sync - the duration of synchronization.
- Display Area is the point in time when information is transmitted.
Timeliness looks like this:

We will use the VGA 800x600 mode at 60 Hz, so the parameters are as follows (
parameters for other modes ):
Clock frequency 40 MHz
Horizontal sync:
- Front porch 40 pixels
- Back porch 88 pixels
- Sync 128 pixels
- Display Area 800 pixels
Vertical sync:
- Front porch 1 lines
- Back porch 23 lines
- Sync 4 lines
- Display Area 600 lines
FPGA Firmware
To get the firmware, we need the following modules in Qsys:
- HPS is our processor.
- Frame Reader is an IP core that reads frames stored in external memory and displays them as a video stream.
- Clocked Video Output is an IP core, from Avalon-ST it makes a VGA output in a similar format.
- Altera PLL - PLL to change the clock frequency: we need to get 40 MHz from 25 MHz, which is on the board.
The processor comes with
AXI H2F and F2H interfaces, the Altera IP cores have
Avalon-ST and Avalon-MM interfaces, so an Interconnect module is also needed, which must convert from one interface to another and multiplex data streams. It will appear automatically when generating files.
Learn more about Frame Reader and Clocked Video Output
here .
How to assemble the firmware and what settings are needed for HPS can be found in this
article .
Altera pll
PLL settings.
Frame reader
Here are configured:
- FIFO Parameters at the Module Input
- Data transfer settings
- Number of active pixels
The parameters
Bits per pixel per color plane and
Number of color planes in parallel are associated with the driver and are explained below. Please note that the dimension of the interface with HPS coincides with the dimension of the
Master port width .
Clocked Video Output
Here:
- Sync settings (vga_vs_o, vga_hs_o), which were described above
- The way in which the data comes (it is the same as that of the Frame Reader)
Qsys Connections
And now we connect everything. Settings for the Frame Reader module are “hooked” to the h2f master, an interface for transferring f2h slave data. We connect Clocked Video Output with Frame Reader avalon_streaming_source -> din. Everything is outclk0 clocked.
And generate files by clicking
Generate HDL ....As mentioned above, the board is 16 bits, and 32 bits come out of the module, so you need to carefully assign the pins in the qsf file, or edit the output for yourself conveniently in the top project file. We need high bits of each color, they are more informative than low bits.
Please note that this is the first and only place where we edit the code. This is no longer required.
logic vga_v_sync; logic vga_h_sync; logic [31:0] vga_data; logic [7:0] vid_r; logic [7:0] vid_g; logic [7:0] vid_b; assign vga_r_o = vid_r[7:3]; assign vga_g_o = vid_g[7:2]; assign vga_b_o = vid_b[7:3]; assign vga_hs_o = vga_h_sync; assign vga_vs_o = vga_v_sync; assign { vid_r, vid_g, vid_b } = vga_data;
Driver and dtb
We need an
altvipfb driver.
Let us return to the parameters
Bits per pixel per color plane and
Number of color planes in parallel in
Frame Reader . The driver says:
if (bits_per_color != 8) { dev_err(&fbdev->pdev->dev, "bits-per-color is set to %i. Curently only 8 is supported.", bits_per_color); return -ENODEV; } if (!(fbdev->mem_word_width >= 32 && fbdev->mem_word_width % 32 == 0)) { dev_err(&fbdev->pdev->dev, "mem-word-width is set to %i. must be >= 32 and multiple of 32.", fbdev->mem_word_width); return -ENODEV; }
The number of bits per color is only 8 and the word width must be greater or a multiple of 32. What is the reason for this restriction? Look further and see:
info->var.red.offset = 16; info->var.red.length = 8; info->var.red.msb_right = 0; info->var.green.offset = 8; info->var.green.length = 8; info->var.green.msb_right = 0; info->var.blue.offset = 0; info->var.blue.length = 8; info->var.blue.msb_right = 0;
It becomes clear that the driver works in the
True color mode, writing the color in a 32-bit word (it is more convenient to align than 24), and it works only in this mode.
In order to build this driver, the following changes need to be made in the kernel config.
CONFIG_FB_CFB_FILLRECT=m CONFIG_FB_CFB_COPYAREA=m CONFIG_FB_CFB_IMAGEBLIT=m CONFIG_FB_ALTERA_VIP=m
In order for linux to know that we have a frame from Altera in the FPGA, the following magic words need to be written in dtb:
hps_0_h2f: bridge@0xc0000000 { compatible = "altr,bridge-1.0", "simple-bus"; reg = < 0xc0000000 0x20000000 >; #address-cells = < 1 >; #size-cells = < 1 >; ranges = <0x00000000 0xc0000000 0x4080 >; alt_vip_vfr_1: vip2@0x0 { compatible = "ALTR,vip-frame-reader-13.0", "ALTR,vip-frame-reader-9.1"; reg = < 0x4000 0x00000080 >; max-width = < 800 >; /* MAX_IMAGE_WIDTH type NUMBER */ max-height = < 600 >; /* MAX_IMAGE_HEIGHT type NUMBER */ mem-word-width = < 0x80 >; bits-per-color = < 0x8 >; }; };
In the
range parameter, the range of valid addresses from which the driver will read, and in
reg = <0x4000 0x00000080> , the starting address and how many addresses are occupied by alt_vip.
Mem-word-width is the
Master port width parameter in
Frame Reader .
Read more about
dtb .
Starting the terminal and X's
Go to the device and load the driver:
modprobe altvipfb modprobe fbcon
Then we check if everything is good with the help of dmesg, and see if there is a similar line:
[ 66.424283] altvipfb c0000000.vip2: fb0: altvipfb frame buffer device at 0x2c000000+0x12c000
Hooray! Fb0 appeared:
ls -l /dev/fb0 crw-rw---T 1 root fb 29, 0 Nov 26 10:13 /dev/fb0
Then we display the console on the screen connected to the board:
/sbin/getty 38400 tty1
Set
icewm and start with startx:
apt-get install icewm icewm-themes startx
Total: we got a graphics controller, with minimal knowledge of HDL languages.