summaryrefslogtreecommitdiffstats
path: root/scanconv.v
blob: cb7a5fbe62746c87ae573db0a1d1cad4ce155215 (plain)
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
//
// scanconv.v
//
// Converts ABC806 288p50 (only 250 lines ever used)
// to 768p50 by tripling each scan line; this seems to work on modern
// monitors. This does NOT attempt to separate hsync and vsync since
// composite sync on the hsync line seems to work on all monitors I have
// tested it on, but it would be possible to do so fairly easily.
//
// The input 72 MHz clock is obtained from the software PLL which produces
// a clock frequency-locked to hsync.
//
module scanconv (
		 input rst_n,
		 input clk,	// 72 MHz clock = 6 x pixel clock (12 MHz)

		 input abc_r,
		 input abc_g,
		 input abc_b,
		 input abc_s,	// Composite sync

		 output vga_r,
		 output vga_g,
		 output vga_b,
		 output vga_s	// Still composite sync
		 );

   wire [3:0] abc_srgb;	        // sync, red, blue, green
   wire [3:0]  in_srgb;		// deglitched

   assign abc_srgb = {abc_s, abc_r, abc_g, abc_b};
   assign in_srgb = abc_srgb;

   // Start of hsync is marked by an edge in the input sync after at least
   // 2/3 a scanline. This strobe is held for one clock cycle regardless
   // of phase. This is the point where we need to reset the phase counter
   // and flip page buffers.
   reg	       prev_sync;
   wire        hsync_ok;
   wire        sync_stb = (in_srgb[3] ^ prev_sync);

   always @(posedge clk or negedge rst_n)
     if (~rst_n)
       prev_sync <= 1'b0;
     else
       prev_sync <= in_srgb[3];

   wire  hsync_stb = hsync_ok & sync_stb;

   reg  [2:0] phase;		// Clock cycle within pixel
   reg  [9:0] in_x;	        // Current x coordinate
   reg  [9:0] out_x;		// Current outpuet pixel
   reg        inpage;	        // Current input RAM page
   wire       outpage = ~inpage;

   assign hsync_ok = in_x[9];

   wire [2:0] sample_at = 3'd2;

   always @(posedge clk or negedge rst_n)
     if (~rst_n)
       begin
	  phase      <= 3'b0;
	  in_x       <= 10'b0;
	  out_x      <= 10'b0;
	  inpage     <=  1'b0;
       end
     else
       begin
	  if (hsync_stb)
	    begin
	       phase    <= 3'b0;
	       in_x     <= 10'b0;
	       out_x    <= 10'b0;
	       inpage   <= ~inpage;
	    end
	  else
	    begin
	       phase <= phase + 1'b1;

	       if (phase == 3'd5)
		 begin
		    in_x  <= in_x + 1'b1;
		    phase <= 3'd0;
		 end

	       if (phase[0])
		 begin
		    out_x[7:0] <= out_x[7:0] + 1'b1;
		    if (&out_x[7:0])
		      begin
			 if (out_x[9])
			   out_x[9:8] <= 2'b00;
			 else
			   out_x[9:8] <= out_x[9:8] + 1'b1;
		      end
		 end
	    end
       end

   wire [3:0] ram_srgb;
   wire [3:0] out_srgb = ram_srgb;

   wire  sample_en = (phase == sample_at);

   ram_linebuf ram_linebuf (
			    .clock     ( clk ),

			    .data      ( in_srgb ),
			    .wraddress ( { inpage, in_x } ),
			    .wren      ( sample_en ),

			    .rdaddress ( { outpage, out_x } ),
			    .q         ( ram_srgb )
			    );

   assign vga_s = out_srgb[3];
   assign vga_r = out_srgb[2];
   assign vga_g = out_srgb[1];
   assign vga_b = out_srgb[0];

endmodule // scanconv