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
|
`include "gfx/gfx_defs.sv"
module gfx_scanout
(
input logic clk,
rst_n,
input logic enable_clear,
input rgb24 clear_color,
input logic mask,
output linear_coord mask_addr,
input logic fb_waitrequest,
fb_readdatavalid,
input vram_word fb_readdata,
output logic fb_read,
output half_coord 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 <= mask_out_addr;
if (mask_fifo_ready)
put_mask <= effective_mask;
end
endmodule
|