The
SOCoCo-80 Project |
|
|
|
I plan on updating the main page of this blog with more details as I go forward. Each section of this blog will cover specific aspects of the project. This section will cover the details of the video controller as is morphs into reality.
|
|
The Video
|
|
To create the timing needed for most VGA monitors we must start with the proper clock. The VGA standard calls for a clock frequency of 25.175 MHz or a 28.322 MHz master. The more common standard is the 25.175 MHz so I will be using that as my base frequency. To accomplish this I will use a Megafunction Phase Lock Loop which can take a base clock and create up to three output clocks which run at a harmonic frequency of the base. Although this magic Megafunction works well, it’s not perfect. The planned output frequency is not exactly 25.175 MHz. This causes some problems which will be detailed later.
To create a 640x480 pixel resolution frame we need to generate the horizontal frame. This consists of a total of 800 clock pulse. 640 of those pulses will be for the video. 16 for the front porch delay. 96 for the Horizontal Sync. Pulse width and finally 48 for the back porch.
I used this site as a reference to generate the signals:
If you look at the code below, there’s an additional 7 clock cycles called the H_SYNC_DELAY for a total of 807 clock cycles. This is to correct for the Phase Lock Loop variance on the 25.175 MHz clock. It’s actually about 25.2 MHz which is slightly faster.
The Vertical timing is a slave function of the horizontal. In other words, the counting of the vertical is based on the number of horizontal scans and not the main clock. The VGA standard calls for a total of 525 horizontal scans to make up the total vertical frame. I have added one more scan frame to make up for the clock frequency variance. Doing this was the preferred method because just adding more delay to the horizontal caused my monitor to loose sync. occasionally.
The code below work perfectly but handles only one video mode (640 x 480 x 64 colors). The plan is to expand this controller to many other modes, so more to come!!!
|
|
The Code |
|
// //############################################################## // //
This source code is the proprty of Tim Franklin //
Modifications to this code is prohibited under //
all software copyright laws. So don't do it!!! // //
support@franklinlabs.com // //
Rev: 1.0.0.014 // //############################################################## // module VideoController (
input
i_VGA_CLK,
input
i_RST_N, output [
3:0 ] o_Red, output [
3:0 ] o_Green, output [
3:0 ] o_Blue,
output reg
o_VGA_H_SYNC,
output reg
o_VGA_V_SYNC, //
Memory Control Signals input [
15:0 ] i_VGA_DATA, output [
15:0 ] o_VGA_DATA, output [
17:0 ] o_VGA_ADDR,
output reg
o_VGA_WE_N,
output reg
o_VGA_OE_N,
output reg
o_VGA_CE_N,
output reg
o_VGA_LB_N,
output reg
o_VGA_UB_N ); // //############################################################## // // //
============================================================ //
Parameter Definitions //
============================================================ // parameter
VIDEO_WIDTH = 640;
parameter VIDEO_HEIGHT
= 480;
parameter H_SYNC_VIDEO
= VIDEO_WIDTH;
parameter H_SYNC_FRONT
= 16;
parameter
H_SYNC_CYC = 96;
parameter
H_SYNC_BACK = 48;
parameter H_SYNC_DELAY
= 7;
parameter
H_TIME_ACT =
H_SYNC_VIDEO;
parameter H_TIME_FRONT
= H_TIME_ACT + H_SYNC_FRONT;
parameter
H_TIME_CYC =
H_TIME_FRONT + H_SYNC_CYC;
parameter H_TIME_BACK
= H_TIME_CYC + H_SYNC_BACK;
parameter
H_TIME_DELAY = H_TIME_BACK +
H_SYNC_DELAY;
parameter V_SYNC_VIDEO
= VIDEO_HEIGHT;
parameter V_SYNC_FRONT
= 31;
parameter
V_SYNC_CYC = 2;
parameter V_SYNC_BACK
= 11;
parameter V_SYNC_DELAY
= 2;
parameter
V_TIME_ACT =
V_SYNC_VIDEO;
parameter V_TIME_FRONT
= V_TIME_ACT + V_SYNC_FRONT;
parameter
V_TIME_CYC =
V_TIME_FRONT + V_SYNC_CYC; parameter
V_TIME_BACK = V_TIME_CYC +
V_SYNC_BACK;
parameter V_TIME_DELAY
= V_TIME_BACK + V_SYNC_DELAY;
parameter
BLANK_ON =
1;
parameter
BLANK_OFF = 0;
parameter
H_SYNC_ON = 0; parameter
H_SYNC_OFF = 1;
parameter
V_SYNC_ON = 0;
parameter
V_SYNC_OFF = 1;
parameter SYNC_STATE_RESET = 4'd0;
parameter SYNC_STATE_VIDEO = 4'd1;
parameter SYNC_STATE_FRONT = 4'd2;
parameter SYNC_STATE_SYNC = 4'd3;
parameter SYNC_STATE_BACK = 4'd4;
parameter SYNC_STATE_DELAY = 4'd5; // //
============================================================ //
Declarations //
============================================================ // reg [ 3:0 ] r_ColorR; reg [ 3:0 ] r_ColorG; reg [ 3:0 ] r_ColorB; reg [ 3:0 ] H_State; reg [ 3:0 ] V_State; reg [ 15:0 ] r_Coord_X; reg [ 15:0 ] r_Coord_Y; reg [ 15:0 ] r_HCount; reg [ 15:0 ] r_VCount; reg
r_VBlank; reg
r_HBlank; reg [ 18:0 ] r_VGA_Address; wire [
7:0 ] w_ImageData; wire [
1:0 ] w_PIX16_Rc; wire [
1:0 ] w_PIX16_Gc; wire [
1:0 ] w_PIX16_Bc; // //
============================================================ //
Assignments - Asynchronous //
============================================================ // assign o_VGA_ADDR [ 17:0 ] = r_VGA_Address[
18:1 ]; assign w_ImageData = r_VGA_Address[ 0 ] ? i_VGA_DATA
[ 7:0 ] : i_VGA_DATA [ 15:8 ]; // 16
color mode data aquisition assign
w_PIX16_Rc = w_ImageData[ 1:0 ]; assign
w_PIX16_Gc = w_ImageData[ 3:2 ]; assign
w_PIX16_Bc = w_ImageData[ 5:4 ]; assign o_Red =
r_HBlank ? 4'b0000 : r_VBlank ? 4'b0000 : r_ColorR; assign o_Green = r_HBlank ? 4'b0000 : r_VBlank ? 4'b0000 : r_ColorG; assign o_Blue = r_HBlank ? 4'b0000 : r_VBlank ? 4'b0000 : r_ColorB; assign o_VGA_DATA = 16'hzzzz; // //
============================================================ //
Methods - Synchronous //
============================================================ // always@
( posedge i_VGA_CLK or negedge i_RST_N ) begin
o_VGA_WE_N = 1;
o_VGA_CE_N = 0;
o_VGA_OE_N = 0;
o_VGA_UB_N = 0;
o_VGA_LB_N = 0;
r_VGA_Address <= ( r_Coord_Y
* VIDEO_WIDTH ) + r_Coord_X;
//
// ============================================================
// This code displays the pixels during the scan time and
// turns them off during the blank pulses
// ============================================================
//
if ( !i_RST_N )
begin
r_ColorR
<= 0;
r_ColorG
<= 0;
r_ColorB
<= 0;
r_Coord_X <=
0;
r_Coord_Y <=
0;
r_HCount
<= -1;
r_VCount
<= 0;
o_VGA_H_SYNC <= V_SYNC_OFF;
o_VGA_V_SYNC <= V_SYNC_OFF;
r_HBlank
<= BLANK_ON;
r_VBlank
<= BLANK_ON;
H_State
<= SYNC_STATE_RESET;
V_State
<= SYNC_STATE_RESET;
end
else
begin
r_HCount = r_HCount + 1;
// ============================================================
// Generate Horizontal sync timing
// ============================================================
case( H_State )
SYNC_STATE_RESET:
begin
r_ColorR <= 0;
r_ColorG <= 0;
r_ColorB
<= 0;
r_HBlank <= BLANK_OFF;
o_VGA_H_SYNC <= H_SYNC_OFF;
H_State <= SYNC_STATE_VIDEO;
end
SYNC_STATE_VIDEO:
begin r_Coord_X <= r_HCount;
if( r_HCount >= H_TIME_ACT )
begin
r_HBlank <= BLANK_ON;
o_VGA_H_SYNC <= H_SYNC_OFF;
H_State
<= SYNC_STATE_FRONT;
end
end
SYNC_STATE_FRONT:
begin
if( r_HCount >= ( H_TIME_FRONT ) )
begin
r_HBlank <= BLANK_ON;
o_VGA_H_SYNC <= H_SYNC_ON;
H_State <= SYNC_STATE_SYNC;
end
end
SYNC_STATE_SYNC:
begin
if( r_HCount >= ( H_TIME_CYC ) )
begin
r_HBlank <= BLANK_ON;
o_VGA_H_SYNC <= H_SYNC_OFF;
H_State <= SYNC_STATE_BACK;
end
end
SYNC_STATE_BACK:
begin
if( r_HCount >= ( H_TIME_BACK ) )
begin
r_HBlank <= BLANK_ON;
o_VGA_H_SYNC <= H_SYNC_OFF;
H_State <= SYNC_STATE_DELAY;
end
end
SYNC_STATE_DELAY:
begin
if( r_HCount >= ( H_TIME_DELAY ) )
begin
r_HBlank <= BLANK_OFF;
o_VGA_H_SYNC <= H_SYNC_OFF;
H_State <= SYNC_STATE_VIDEO;
r_HCount = 0;
r_Coord_X <= 0;
r_VCount = r_VCount + 1;
end
end
endcase
// ============================================================
// Generate Vertical sync timing
// ============================================================
case( V_State )
SYNC_STATE_RESET:
begin
r_VBlank <= BLANK_OFF;
o_VGA_V_SYNC <= V_SYNC_OFF;
V_State <= SYNC_STATE_VIDEO;
end
SYNC_STATE_VIDEO:
begin
r_Coord_Y <= r_VCount;
if( r_VCount >= V_TIME_ACT )
begin
r_VBlank <= BLANK_ON;
o_VGA_V_SYNC <= V_SYNC_OFF;
V_State <= SYNC_STATE_FRONT;
end
end
SYNC_STATE_FRONT:
begin
if( r_VCount >= ( V_TIME_FRONT ) )
begin
r_VBlank <= BLANK_ON;
o_VGA_V_SYNC <= V_SYNC_ON;
V_State <= SYNC_STATE_SYNC;
end
end
SYNC_STATE_SYNC:
begin
if( r_VCount >= ( V_TIME_CYC ) )
begin
r_VBlank <= BLANK_ON;
o_VGA_V_SYNC <= V_SYNC_OFF;
V_State <= SYNC_STATE_BACK;
end
end
SYNC_STATE_BACK:
begin
if( r_VCount >= ( V_TIME_BACK ) )
begin
r_VBlank <= BLANK_ON;
o_VGA_V_SYNC <= V_SYNC_OFF;
V_State <= SYNC_STATE_DELAY;
end
end
SYNC_STATE_DELAY:
begin
if( r_VCount >= ( V_TIME_DELAY ) )
begin
r_VBlank <= BLANK_OFF;
o_VGA_V_SYNC <= V_SYNC_OFF;
V_State <= SYNC_STATE_VIDEO;
r_VCount = 0;
r_Coord_Y <= 0;
end
end
endcase
end
//
// ============================================================
// This code populated the three color registers depending on
// what color mode we are in
// ============================================================
//
case ( w_PIX16_Rc )
2'b00: r_ColorR <=
4'b0000;
2'b01: r_ColorR <=
4'b0011;
2'b10: r_ColorR <=
4'b0111;
2'b11: r_ColorR
<= 4'b1111;
endcase
case ( w_PIX16_Gc )
2'b00: r_ColorG <=
4'b0000;
2'b01: r_ColorG <=
4'b0011;
2'b10: r_ColorG <=
4'b0111;
2'b11: r_ColorG <=
4'b1111;
endcase
case ( w_PIX16_Bc )
2'b00: r_ColorB <=
4'b0000;
2'b01: r_ColorB <=
4'b0011;
2'b10: r_ColorB <=
4'b0111;
2'b11: r_ColorB <=
4'b1111;
endcase end endmodule |
|
© 2013 - FranklinLabs www.franklinlabs.com |
No comments:
Post a Comment