-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathvga.v
133 lines (104 loc) · 3.59 KB
/
vga.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// A simple system-on-a-chip (SoC) for the MiST
// (c) 2015 Till Harbaum
// VGA controller generating 160x100 pixles. The VGA mode ised is 640x400
// combining every 4 row and column
// http://tinyvga.com/vga-timing/640x400@70Hz
module vga (
// pixel clock
input pclk,
// CPU interface (write only!)
input cpu_clk,
input cpu_wr,
input [13:0] cpu_addr,
input [7:0] cpu_data,
// 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 <= 0;
else h_cnt <= h_cnt + 1;
// generate negative hsync signal
if(h_cnt == H+HFP) hs <= 1'b0;
if(h_cnt == H+HFP+HS) hs <= 1'b1;
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 <= 0;
else v_cnt <= v_cnt + 1;
// generate positive vsync signal
if(v_cnt == V+VFP) vs <= 1'b1;
if(v_cnt == V+VFP+VS) vs <= 1'b0;
end
end
// read VRAM
reg [13:0] video_counter;
reg [7:0] pixel;
reg de;
// 16000 bytes of internal video memory for 160x100 pixel at 8 Bit (RGB 332)
reg [7:0] vmem [160*100-1:0];
// write VRAM via CPU interface
always @(posedge cpu_clk)
if(cpu_wr)
vmem[cpu_addr] <= cpu_data;
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
// pixel <= video_counter[7:0]; // color pattern
pixel <= vmem[video_counter]; // read VRAM
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 };
// split the 8 rgb bits into the three base colors. Every second line is
// darker to give some scanlines effect
//assign r = (!v_cnt[0])?{ pixel[7:5], 3'b00000 }:{ 1'b0, pixel[7:5], 2'b0000 };
//assign g = (!v_cnt[0])?{ pixel[4:2], 3'b00000 }:{ 1'b0, pixel[4:2], 2'b0000 };
//assign b = (!v_cnt[0])?{ pixel[1:0], 4'b000000 }:{ 1'b0, pixel[1:0], 3'b00000 };
//assign r = 8'h00;
//assign g = 8'hff;
//assign b = 8'h00;
assign VGA_DE = de;
endmodule