Introduction
In order not to buy a defective monitor, it is highly recommended to check it before buying. DE10-Lite is great for this because of a compact size and the presence of a VGA connector.
Hardware and software
DE10-Lite board (based on Altera's MAX 10 FPGA)

Quartus Prime Lite Edition
Verilog HDL
Some words about VGA and clock division
VGA (Video Graphics Array) is an analogue interface used to display visual data on computer monitors. Till a few years back, it was the most used display interface. It is now being slowly replaced by HDMI.
To display an image using VGA, we must follow its specification: timings and clock rate. In this paragraph I will consider only the clock rate. Information about the timing is provided in the next block.
VGA needs 25.175 MHz clock rate, but the DE10-Lite can only provide 10 MHz or 50 MHz clock. Therefore, we must somehow obtain 25.175 MHz by dividing or multiplying the existing clocks. In this project, I will divide 50 MHz clock.
To obtain the required clock, I used Altera Phase-Locked Loop (Altera PLL) from the IP catalogue in Quartus Prime.
Displaying an image on the screen through the VGA protocol
In this project I will use 640x480 at 60 Hz.
Therefore, we have to follow the following timings:

Code of the VGA controller
module VGA(
input clock25MHz,
output vsync,
output hsync,
output canDisplayImage,
output reg[9:0] x,
output reg[9:0] y
);
reg [9:0] xCounter;
reg [9:0] yCounter;
always @(posedge clock25MHz) // horizontal counter
begin
if (xCounter < 799)
xCounter <= xCounter + 1;
else
xCounter <= 0;
end
always @ (posedge clock25MHz) // vertical counter
begin
if (xCounter == 799) // only counts up 1 count after horizontal finishes 800 counts
begin
if (yCounter < 525) // vertical counter (including off-screen vertical 45 pixels) total of 525 pixels
yCounter <= yCounter + 1;
else
yCounter <= 0;
end
end
always @ (posedge clock25MHz)
begin
if(xCounter>=144 && xCounter<784) x=xCounter-144;
else x = 0;
if(yCounter>=35 && yCounter < 515) y = yCounter-35;
else y = 0;
end
assign hsync = (xCounter >= 0 && xCounter < 96) ? 1:0; // hsync high for 96 counts
assign vsync = (yCounter >= 0 && yCounter < 2) ? 1:0; // vsync high for 2 counts
assign canDisplayImage = ((xCounter > 144) && (xCounter <= 783))
&& ((yCounter > 35) && (yCounter <= 514));
endmodule
Now, when the board can display any image on a screen, it's time to draw something.
Using x and y, we can control each pixel. For example, the part of the code that draws a white 100x100 rectangle:
Drawing a 100x100 square
module tvPattern(
input [9:0] x,
input [9:0] y,
output reg [3:0] red,
output reg [3:0] green,
output reg [3:0] blue);
always @(x||y)
begin
if(x>=0 && x<100 && y>=0 && y<100))
begin
red <= 4'hF;
green <= 4'hF;
blue <= 4'hF;
end
end
endmodule
Animated test
I decided to add an animated test (e.g. bouncing square).
The variables for squareX and squareY are created.
Then, in sequential logic block always @(posedge clock25MHz
), I am checking for collisions with screen borders and if collision - change the direction of the square.
Moving square
module bouncingSquare(
input clock25MHz,
input [9:0] x,
input [9:0] y,
output [3:0] red,
output [3:0] green,
output [3:0] blue
);
localparam SQUARE_SIZE = 20;
// Position of a square
reg [9:0] squareX =100;
reg [9:0] squareY = 100;
// Vertical and horizontal speeds
// 1 - positive direction, 0 - negative direction
reg vSpeed = 1;
reg hSpeed = 1;
// Checking for collisions
wire topCollision = (squareY-SQUARE_SIZE==0);
wire bottomCollision = (squareY+SQUARE_SIZE==479);
wire leftCollision = (squareX-SQUARE_SIZE ==0);
wire rightCollision = (squareX+SQUARE_SIZE == 639);
always @(posedge clock25MHz)
begin
if(topCollision) vSpeed<=1;
if(bottomCollision) vSpeed<=0;
if(leftCollision) hSpeed<=1;
if(rightCollision) hSpeed<=0;
counter <= counter + 1;
squareX <= squareX + (hSpeed==1? 1 : -1);
squareY <= squareY + (vSpeed==1? 1 : -1);
end
// Drawing a square
wire square = (squareX-SQUARE_SIZE<=x && squareX+SQUARE_SIZE>=x) && (squareY-SQUARE_SIZE<=y && squareY+SQUARE_SIZE>=y);
assign red = (square) ? 4'hF: 0;
assign green = (square) ? 4'hF: 0;
assign blue = (square) ? 4'hF: 0;
endmodule
The code works fine, but the square moves too fast. Thus, we must slow it down somehow. To do this, I created another reg and implemented in every clock's rising edge. Then, when the value of this reg becomes equal to some constant (SPEED_CONSTANT), I am changing the values of the squareX and squareY.
Slow moving square
module bouncingSquare(
input clock25MHz,
input [9:0] x,
input [9:0] y,
output [3:0] red,
output [3:0] green,
output [3:0] blue
);
localparam SPEED_CONSTANT = 100000;
localparam SQUARE_SIZE = 20;
// Position of a square
reg [9:0] squareX =100;
reg [9:0] squareY = 100;
// Counter for slowing down the animation
reg [30:0] counter;
// Vertical and horizontal speeds
// 1 - positive direction, 0 - negative direction
reg vSpeed = 1;
reg hSpeed = 1;
// Checking for collisions
wire topCollision = (squareY-SQUARE_SIZE==0);
wire bottomCollision = (squareY+SQUARE_SIZE==479);
wire leftCollision = (squareX-SQUARE_SIZE ==0);
wire rightCollision = (squareX+SQUARE_SIZE == 639);
always @(posedge clock25MHz)
begin
if(topCollision) vSpeed<=1;
if(bottomCollision) vSpeed<=0;
if(leftCollision) hSpeed<=1;
if(rightCollision) hSpeed<=0;
counter <= counter + 1;
if (counter == SPEED_CONSTANT)
begin
squareX <= squareX + (hSpeed==1? 1 : -1);
squareY <= squareY + (vSpeed==1? 1 : -1);
end
if(counter>SPEED_CONSTANT) counter<=0;
end
// Drawing a square
wire square = (squareX-SQUARE_SIZE<=x && squareX+SQUARE_SIZE>=x) && (squareY-SQUARE_SIZE<=y && squareY+SQUARE_SIZE>=y);
assign red = (square) ? 4'hF: 0;
assign green = (square) ? 4'hF: 0;
assign blue = (square) ? 4'hF: 0;
endmodule
The video can be found here: link
Switching between different test modes
I used both buttons and switches to display different tests.
Everything worked correctly with switches, however there was a problem with buttons - the tests either changed randomly either didn't change at all. This problem is called Button Bouncing.
Button Bounce happens when you push a mechanical button. When a button is being pushed, it tends to literally bounce upon the metal contact, which connects the circuit.
This effect is shown in the following picture:

To overcome this problem, I used the debouncing technique. You can read more information about it here.
Conclusion
I have described the key stages of creating a monitor tester using an FPGA board and Verilog HDL. Areas of the project's application: business (for example, in a stand for checking monitors in electronics store) and personal use.
This work has been implemented in the scope of «Computer Architecture» course in the Innopolis University.
Some examples
Images


