Page 1 of 1

Really beginner question: video output

Posted: Thu Dec 23, 2021 4:05 pm
by SerErris
Hi all,
I cannot get my head around how video output works on MISTER. In my learning experience I try to follow the book: "Designing Video Game Hardware in Verliog".

The first chapter is to setup a screen output, as this is needed to see anything (whatever you want to display).

So it starts with two simple counters for H-Sync and V-Sync and implements it and setting up a screen with 256x240px.

The next thing is to setup a testpattern and here I struggle on how to implement this.

From the Mister Framework I get the VGA_XYZ signals I can use to write to the Framework.

Code: Select all

	//Base video clock. Usually equals to CLK_SYS.
	output        CLK_VIDEO,

	//Multiple resolutions are supported using different CE_PIXEL rates.
	//Must be based on CLK_VIDEO
	output        CE_PIXEL,

	//Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
	//if VIDEO_ARX[12] or VIDEO_ARY[12] is set then [11:0] contains scaled size instead of aspect ratio.
	output [12:0] VIDEO_ARX,
	output [12:0] VIDEO_ARY,

	output  [7:0] VGA_R,
	output  [7:0] VGA_G,
	output  [7:0] VGA_B,
	output        VGA_HS,
	output        VGA_VS,
	output        VGA_DE,    // = ~(VBlank | HBlank)
	output        VGA_F1,
	output [1:0]  VGA_SL,
	output        VGA_SCALER, // Force VGA scaler
But how do I set the CLK_VIDEO and what is CE_PIXEL rates ? So CE_PIXEL is a divider just of CLK_VIDEO to scale ?

I cannot figure out on how to setup the clks right.

I have implemented the SYNC parameters like that:

Code: Select all

'ifndef HVSYNC_GENERATOR_H
'define HVSYNC_GENERATOR_H
	module hvsync_generator (input clk, input reset, output hsync, output vsync, output display_on,
				output hpos[8:0], output vpos[8:0]);


	//Setup screen parameters
	localparam H_DISPLAY = 256; //horizontal display width
	localparam H_BACK = 23;		//left border (back porch)
	localparam H_FRONT = 7;		//right border (front porch)
	localparam H_SYNC = 23;		//horizontal sync width

	localparam V_DISPLAY = 240; //vertical display hight
	localparam V_TOP = 4 ;		//vertical top border
	localparam V_BOTTOM = 14;	//vertical bottom border
	localparam V_SYNC = 4;		//vertical sync # lines

	//calculate resulting parameters
	localparam H_SYNC_START = H_DISPLAY + H_FRONT;
	localparam H_SYNC_END = H_DISPLAY + H_FRONT + H_SYNC - 1;
	localparam H_MAX = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1;

	localparam V_SYNC_START = V_DISPLAY + V_BOTTOM;
	localparam V_SYNC_END = V_DISPLAY + V_BOTTOM + V_SYNC - 1;
	localparam V_MAX = V_DISPLAY + V_BOTTOM + V_TOP + V_SYNC - 1;

	//horizontal position counter
	always @(posedge clk)
	begin
		hsync <= (hpos>=H_SYNC_START && hpos<=H_SYNC_END);
		if(hmaxxed)
			hpos <= 0;
		else
			hpos <= hpos + 1;
	end

	wire hmaxxed = (hpos == H_MAX) || reset;

	//vertical position counter
	always @(posedge clk)
	begin
		vsync <= (vpos>=V_SYNC_START && vpos<=V_SYNC_END);
		if(vmaxxed)
			vpos <= 0;
		else
			vpos <= vpos + 1;
	end

	wire vmaxxed = (vpos == V_MAX) || reset;

	assign display_on = (hpos<H_DISPLAY) && (vpos<V_DISPLAY)
'endif
This is pretty much a direct implementation of the book example.

So looking at the number of lines and pixels per line and the frequency (60hz) that should give me a CLK_VIDEO of 262 scanlines * 309 pixel * 60hz = 4.857 Mhz.

I can create that clock speed with a PPL, no problem.

But that does not align with any core I have seen ... Those are all having a 24mhz CLK_VIDEO and then doing something else with it

Looking at the tutorials he is setting up a VGA chip, that receives a 25mhz clock (640x480x70hz timings).

I can see why that is working this way as standard resolution for VGA 640x400 with 70hz is requireing that frequency.

What I simply do not understand: Can I just use the 4.857Mhz frequency and do I need ce_pix at all?

So how do both play together?

What is VGA_DE? Visible Area ... why is it even needed ?

Here is the complete module

Code: Select all

module vga (
   // pixel clock
   input  pclk,
   // VGA output
   output reg	hs,
   output reg 	vs,
   output [7:0] r,
   output [7:0] g,
   output [7:0] b,
	output VGA_DE
);
					
// 640x400 70HZ VESA according to  http://tinyvga.com/vga-timing/640x400@70Hz

parameter H   = 640;    // width of visible area
parameter HFP = 16;     // unused time before hsync
parameter HS  = 96;     // width of hsync
parameter HBP = 48;     // unused time after hsync

parameter V   = 400;    // height of visible area
parameter VFP = 12;     // unused time before vsync
parameter VS  = 2;      // width of vsync
parameter VBP = 35;     // unused time after vsync


reg[9:0]  h_cnt;        // horizontal pixel counter
reg[9:0]  v_cnt;        // vertical pixel counter

reg hblank;
reg vblank;

// both counters count from the begin of the visibla area

// horizontal pixel counter
always@(posedge pclk) begin
	if(h_cnt==H+HFP+HS+HBP-1)   h_cnt <= 10'b0;
	else                        h_cnt <= h_cnt + 10'b1;

	// generate negative hsync signal
	if(h_cnt == H+HFP)    hs <= 1'b0;
	if(h_cnt == H+HFP+HS) hs <= 1'b1;
	if(h_cnt == H+HFP+HS) hblank <= 1'b1; else hblank<=1'b0;

	end

// veritical pixel counter
always@(posedge pclk) begin
	// the vertical counter is processed at the begin of each hsync
	if(h_cnt == H+HFP) begin
		if(v_cnt==VS+VBP+V+VFP-1)  v_cnt <= 10'b0; 
		else							   v_cnt <= v_cnt + 10'b1;

	        // generate positive vsync signal
		if(v_cnt == V+VFP)    vs <= 1'b1;
		if(v_cnt == V+VFP+VS) vs <= 1'b0;
		if(v_cnt == V+VFP+VS) vblank <= 1'b1; else vblank<=1'b0;
	end
end

// read VRAM
reg [13:0] video_counter;
reg [7:0] pixel;
reg de;

always@(posedge pclk) begin
        // The video counter is being reset at the begin of each vsync.
        // Otherwise it's increased every fourth pixel in the visible area.
        // At the end of the first three of four lines the counter is
        // decreased by the total line length to display the same contents
        // for four lines so 100 different lines are displayed on the 400
        // VGA lines.

	// visible area?
	if((v_cnt < V) && (h_cnt < H)) begin
		if(h_cnt[1:0] == 2'b11)
			video_counter <= video_counter + 14'd1;
		
		pixel <= (v_cnt[2] ^ h_cnt[2])?8'h00:8'hff;    // checkboard
		de<=1;
	end else begin
		if(h_cnt == H+HFP) begin
			if(v_cnt == V+VFP)
				video_counter <= 14'd0;
			else if((v_cnt < V) && (v_cnt[1:0] != 2'b11))
				video_counter <= video_counter - 14'd160;
		de<=0;
		end
			
		pixel <= 8'h00;   // black
	end
end

// seperate 8 bits into three colors (332)
assign r = { pixel[7:5],  3'b00000 };
assign g = { pixel[4:2],  3'b00000 };
assign b = { pixel[1:0], 4'b000000 };

//assign VGA_DE  = ~(hblank | vblank);
assign VGA_DE = de;

endmodule

Re: Really beginner question: video output

Posted: Thu Dec 23, 2021 6:54 pm
by Flandango
I'm not an expert at this but I have played around quite a bit with video signals for the core I am working with, so maybe I can provide some pointers while the you wait for the experts to chime in.
First, you do need CE_PIX. Typically that's the clock that you want your video at (25mhz for typical vga, 4.857 for your resolution).
The CLK_VIDEO should be a multiple of CE_PIX and is usually the Core clock (ex clk_sys), in your case you can try a clock speed of 19.428 (4x your clock).
VGA_DE, just as you suspect, is on when there is "visible" data for the screen (not part of the v/h blank period) and is needed.

So for your module, create two clocks, the main clock (outclk_0 / clk_sys) set it as 19.428 and assign CLK_VIDEO to it.
The second clock (outclk_1) set it to 4.857 (call it whatever you want....ex: clk_4m8), assign that to CE_PIXEL.
Then assign the VGA_* to appropriate output signals from your module and give it a try.

Re: Really beginner question: video output

Posted: Thu Dec 23, 2021 9:26 pm
by SerErris
Great, thanks for the explanation. I will give this a try. Also I will use only one clock from PLL and setup a divider to stay in phase and ensure that the video clock is in sync with the CE_PIX clock.

Keep you posted on this.