summaryrefslogtreecommitdiff
path: root/rtl/legacy_gfx/gfx_scanout.sv
blob: a43d14c80a9eaae500da9de999e90d5573ba10e3 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
`include "gfx/gfx_defs.sv"

module gfx_scanout
(
	input  logic        clk,
	                    rst_n,

	input  logic        enable_clear,
	input  rgb24        clear_color,
	input  vram_addr    scan_base,

	input  logic        mask,
	output linear_coord mask_addr,

	input  logic        fb_waitrequest,
	                    fb_readdatavalid,
	input  vram_word    fb_readdata,
	output logic        fb_read,
	output vram_addr    fb_address,

	input  logic        scan_ready,
	output logic        scan_valid,
	                    scan_endofpacket,
	                    scan_startofpacket,
	output rgb30        scan_data,

	output logic        vsync
);

	logic commit, effective_mask, flush, mask_fifo_out, dac_ready,
	      fb_ready, mask_fifo_ready, fb_fifo_valid, mask_fifo_valid,
	      pop, put, put_mask, next_vsync, start_vsync, wait_vsync;

	vram_word fb_fifo_out;
	half_coord commit_addr, mask_in_addr, mask_out_addr, mask_hold_addr, max_addr;

	assign mask_addr = mask_in_addr[$bits(mask_in_addr) - 1:$bits(mask_in_addr) - $bits(mask_addr)];
	assign max_addr[0] = 1;
	assign max_addr[$bits(max_addr) - 1:1] = `GFX_X_RES * `GFX_Y_RES - 1;

	assign fb_ready = !fb_read || !fb_waitrequest;
	assign next_vsync = commit && start_vsync;
	assign start_vsync = mask_hold_addr == max_addr;
	assign effective_mask = mask || !enable_clear;

	gfx_flush_flow #(.STAGES(`GFX_MASK_STAGES)) mask_flow
	(
		.in_valid(!wait_vsync),
		.out_ready(fb_ready && mask_fifo_ready && !next_vsync),
		.out_valid(pop),
		.*
	);

	gfx_pipes #(.WIDTH($bits(mask_in_addr)), .DEPTH(`GFX_MASK_STAGES)) addr_pipes
	(
		.in(mask_in_addr),
		.out(mask_out_addr),
		.stall(0),
		.*
	);

	/* Estas FIFOs deben cumplir dos propiedades para garantizar correctitud:
	 *
	 * 1. mask_fifo.out_ready && mask_fifo.out_valid <=> scan.in_ready && scan.in_valid
	 * 2. fb_fifo.out_ready && fb_fifo.out_valid => scan.in_ready && scan.in_valid
	 *
	 * Nótese la asimetría (<=> vs =>), debido a mask_fifo.out
	 */

	gfx_fifo #(.WIDTH($bits(effective_mask)), .DEPTH(`GFX_SCANOUT_FIFO_DEPTH)) mask_fifo
	(
		.in(put_mask),
		.out(mask_fifo_out),
		.in_ready(mask_fifo_ready),
		.in_valid(put),
		.out_ready(dac_ready && (!mask_fifo_out || fb_fifo_valid)),
		.out_valid(mask_fifo_valid),
		.*
	);

	// 2x para evitar potencial overflow cuando fb_read=1 pero mask_fifo está llena
	gfx_fifo #(.WIDTH($bits(vram_word)), .DEPTH(2 * `GFX_SCANOUT_FIFO_DEPTH)) fb_fifo
	(
		.in(fb_readdata),
		.out(fb_fifo_out),
		.in_ready(), // readdatavalid no soporta backpressure
		.in_valid(fb_readdatavalid),
		.out_ready(dac_ready && mask_fifo_valid && mask_fifo_out),
		.out_valid(fb_fifo_valid),
		.*
	);

	gfx_scanout_dac dac
	(
		.in_ready(dac_ready),
		.in_valid(mask_fifo_valid && (!mask_fifo_out || fb_fifo_valid)),
		.*
	);

	always_ff @(posedge clk or negedge rst_n)
		if (!rst_n) begin
			put <= 0;
			fb_read <= 0;
			wait_vsync <= 0;
			commit_addr <= 0;
			mask_in_addr <= 0;
		end else begin
			mask_in_addr <= mask_in_addr + 1;

			if (flush || wait_vsync)
				mask_in_addr <= commit_addr;

			if (commit) begin
				wait_vsync <= start_vsync;
				commit_addr <= start_vsync ? 0 : mask_out_addr;
			end

			if (fb_ready)
				fb_read <= mask_fifo_ready && pop && !next_vsync && effective_mask;

			if (mask_fifo_ready)
				put <= fb_ready && pop && !next_vsync;

			if (vsync)
				wait_vsync <= 0;
		end

	always_ff @(posedge clk) begin
		mask_hold_addr <= mask_out_addr;

		if (fb_ready)
			fb_address <= scan_base + {5'd0, mask_out_addr};

		if (mask_fifo_ready)
			put_mask <= effective_mask;
	end

endmodule