summaryrefslogtreecommitdiff
path: root/rtl/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/gfx')
-rw-r--r--rtl/gfx/gfx_defs.sv6
-rw-r--r--rtl/gfx/gfx_flush_flow.sv45
-rw-r--r--rtl/gfx/gfx_scanout.sv210
-rw-r--r--rtl/gfx/gfx_scanout_dac.sv116
4 files changed, 245 insertions, 132 deletions
diff --git a/rtl/gfx/gfx_defs.sv b/rtl/gfx/gfx_defs.sv
index 2211c52..dc5f5c4 100644
--- a/rtl/gfx/gfx_defs.sv
+++ b/rtl/gfx/gfx_defs.sv
@@ -41,6 +41,7 @@ typedef logic[19:0] half_coord;
`define COLOR_CHANNELS 4
typedef logic[7:0] color8;
+typedef logic[9:0] color10;
typedef struct packed
{
@@ -49,7 +50,7 @@ typedef struct packed
typedef struct packed
{
- logic[9:0] r, g, b;
+ color10 r, g, b;
} rgb30;
typedef struct packed
@@ -92,7 +93,8 @@ typedef logic[8:0] coarse_dim;
`define GFX_MASK_SRAM_STAGES 3
`define GFX_MASK_STAGES (1 + `GFX_MASK_SRAM_STAGES + 1)
-`define GFX_SCAN_STAGES 3 // Ajustable
+
+`define GFX_SCANOUT_FIFO_DEPTH 16 // Ajustable
`define GFX_SETUP_BOUNDS_STAGES 3
`define GFX_SETUP_EDGE_STAGES (1 + `FIXED_FMA_DOT_STAGES)
diff --git a/rtl/gfx/gfx_flush_flow.sv b/rtl/gfx/gfx_flush_flow.sv
new file mode 100644
index 0000000..a0e43d7
--- /dev/null
+++ b/rtl/gfx/gfx_flush_flow.sv
@@ -0,0 +1,45 @@
+module gfx_flush_flow
+#(parameter STAGES=0)
+(
+ input logic clk,
+ rst_n,
+
+ input logic in_valid,
+ out_ready,
+
+ output logic out_valid,
+ commit,
+ flush
+);
+
+ logic was_valid, was_ready;
+ logic[STAGES - 1:0] valid;
+
+ assign flush = was_valid && !was_ready;
+ assign commit = was_valid && was_ready;
+ assign out_valid = valid[STAGES - 1] && !flush;
+
+ always_ff @(posedge clk or negedge rst_n)
+ if (!rst_n) begin
+ was_ready <= 0;
+ was_valid <= 0;
+
+ for (integer i = 0; i < STAGES; ++i)
+ valid[i] <= 0;
+ end else begin
+ was_ready <= out_ready;
+ was_valid <= out_valid;
+
+ if (!flush)
+ valid[0] <= in_valid;
+ else
+ valid[0] <= 0;
+
+ for (integer i = 1; i < STAGES; ++i)
+ if (!flush)
+ valid[i] <= valid[i - 1];
+ else
+ valid[i] <= 0;
+ end
+
+endmodule
diff --git a/rtl/gfx/gfx_scanout.sv b/rtl/gfx/gfx_scanout.sv
index b315e44..074a75a 100644
--- a/rtl/gfx/gfx_scanout.sv
+++ b/rtl/gfx/gfx_scanout.sv
@@ -26,155 +26,105 @@ module gfx_scanout
output logic vsync
);
- logic[`GFX_SCAN_STAGES:0] fb_ready, fb_valid, src_ready, src_valid, src_pipes;
- logic[`GFX_SCAN_STAGES - 1:0] fb_stalls, src_stalls;
- logic[`GFX_MASK_STAGES - 1:0] request_valid;
- logic[$clog2(`GFX_SCAN_STAGES) - 1:0] queued;
+ 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;
- logic effective_mask, foreground, foreground_valid, partial, put_fb_valid, put_src_valid,
- queued_dec, queued_inc, read_half, read_valid, request_flush;
+ mem_word fb_fifo_out;
+ half_coord mask_in_addr, mask_hold_addr, mask_out_addr, max_addr, commit_addr;
- rgb24 fb_pipes[`GFX_SCAN_STAGES + 1], scan_pixel;
- mem_word half;
- half_coord commit_pos, next_pos, read_pos, request_pos[`GFX_MASK_STAGES - 1:0];
- linear_coord scan_pos, last_pos;
+ 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 last_pos = `GFX_LINEAR_RES - 1;
- assign scan_data.r = {scan_pixel.r, {2{scan_pixel.r[0]}}};
- assign scan_data.g = {scan_pixel.g, {2{scan_pixel.g[0]}}};
- assign scan_data.b = {scan_pixel.b, {2{scan_pixel.b[0]}}};
-
- assign scan_pixel = foreground ? fb_pipes[`GFX_SCAN_STAGES] : clear_color;
- assign scan_valid = foreground_valid && (!foreground || fb_valid[`GFX_SCAN_STAGES]);
- assign scan_endofpacket = scan_pos == last_pos;
- assign scan_startofpacket = scan_pos == 0;
-
- assign foreground = src_pipes[`GFX_SCAN_STAGES];
- assign foreground_valid = src_valid[`GFX_SCAN_STAGES];
-
- // Soluciona Error-BLKANDNBLK en Verilator
- assign fb_valid[0] = put_fb_valid;
- assign src_valid[0] = put_src_valid;
-
- assign fb_ready[`GFX_SCAN_STAGES] = scan_ready && foreground_valid && foreground;
- assign src_ready[`GFX_SCAN_STAGES] = scan_ready && scan_valid;
-
- assign next_pos = request_flush ? commit_pos : read_pos;
- assign mask_addr = read_pos[$bits(read_pos) - 1:1];
-
- assign read_half = request_pos[`GFX_MASK_STAGES - 1][0];
- assign read_valid = request_valid[`GFX_MASK_STAGES - 1];
- assign request_flush = (fb_read && fb_waitrequest) || (src_valid[0] && !src_ready[0]) || queued == `GFX_SCAN_STAGES || vsync;
-
- assign queued_inc = !request_flush && read_valid && read_half && effective_mask;
- assign queued_dec = fb_ready[`GFX_SCAN_STAGES - 1] && fb_valid[`GFX_SCAN_STAGES - 1];
+ assign fb_ready = !fb_read || !fb_waitrequest;
assign effective_mask = mask || !enable_clear;
- genvar i;
- generate
- for (i = 0; i < `GFX_SCAN_STAGES; ++i) begin: stages
- gfx_pipeline_flow #(.STAGES(1)) fb_flow
- (
- .stall(fb_stalls[i]),
- .in_ready(fb_ready[i]),
- .in_valid(fb_valid[i]),
- .out_ready(fb_ready[i + 1]),
- .out_valid(fb_valid[i + 1]),
- .*
- );
-
- gfx_pipeline_flow #(.STAGES(1)) src_flow
- (
- .stall(src_stalls[i]),
- .in_ready(src_ready[i]),
- .in_valid(src_valid[i]),
- .out_ready(src_ready[i + 1]),
- .out_valid(src_valid[i + 1]),
- .*
- );
-
- always_ff @(posedge clk) begin
- if (!fb_stalls[i])
- fb_pipes[i + 1] <= fb_pipes[i];
-
- if (!src_stalls[i])
- src_pipes[i + 1] <= src_pipes[i];
- end
- end
-
- for (i = 1; i < `GFX_MASK_STAGES; ++i) begin: request
- always_ff @(posedge clk or negedge rst_n)
- request_valid[i] <= !rst_n ? 0 : (request_valid[i - 1] && !request_flush);
-
- always_ff @(posedge clk)
- request_pos[i] <= request_pos[i - 1];
- end
- endgenerate
+ gfx_flush_flow #(.STAGES(`GFX_MASK_STAGES)) mask_flow
+ (
+ .in_valid(1),
+ .out_ready(fb_ready && mask_fifo_ready),
+ .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(mem_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
- vsync <= 0;
- queued <= 0;
-
- read_pos <= 0;
- scan_pos <= 0;
- commit_pos <= 0;
-
+ put <= 0;
fb_read <= 0;
- partial <= 0;
-
- put_fb_valid <= 0;
- put_src_valid <= 0;
- request_valid[0] <= 0;
+ commit_addr <= 0;
+ mask_in_addr <= 0;
end else begin
- if (queued_inc && !queued_dec)
- queued <= queued + 1;
- else if (!queued_inc && queued_dec)
- queued <= queued - 1;
-
- if (scan_ready && scan_valid)
- scan_pos <= scan_endofpacket ? 0 : scan_pos + 1;
+ mask_in_addr <= mask_in_addr + 1;
+ if (mask_in_addr == max_addr)
+ mask_in_addr <= 0;
- partial <= partial ^ fb_readdatavalid;
+ if (flush)
+ mask_in_addr <= commit_addr;
- put_fb_valid <= partial && fb_readdatavalid;
- request_valid[0] <= !request_flush;
+ if (commit)
+ commit_addr <= mask_hold_addr;
- read_pos <= next_pos + 1;
- if (next_pos == {last_pos, 1'b1})
- read_pos <= 0;
+ if (fb_ready)
+ fb_read <= mask_fifo_ready && pop && effective_mask;
- if (!fb_waitrequest)
- fb_read <= 0;
-
- if (src_ready[0])
- put_src_valid <= 0;
-
- if (!request_flush) begin
- fb_read <= read_valid && effective_mask;
- put_src_valid <= read_valid && read_half;
-
- if (read_valid)
- commit_pos <= request_pos[`GFX_MASK_STAGES - 1];
- end
-
- vsync <= !vsync && !request_flush && read_valid && request_pos[`GFX_MASK_STAGES - 1] == {last_pos, 1'b1};
+ if (mask_fifo_ready)
+ put <= fb_ready && pop;
end
always_ff @(posedge clk) begin
- request_pos[0] <= read_pos;
+ mask_hold_addr <= mask_out_addr;
- if (!request_flush) begin
- fb_address <= request_pos[`GFX_MASK_STAGES - 1];
- src_pipes[0] <= effective_mask;
- end
+ if (fb_ready)
+ fb_address <= mask_out_addr;
- if (fb_readdatavalid) begin
- if (partial)
- fb_pipes[0] <= {fb_readdata[7:0], half};
- else
- half <= fb_readdata;
- end
+ if (mask_fifo_ready)
+ put_mask <= effective_mask;
end
endmodule
diff --git a/rtl/gfx/gfx_scanout_dac.sv b/rtl/gfx/gfx_scanout_dac.sv
new file mode 100644
index 0000000..c8fe218
--- /dev/null
+++ b/rtl/gfx/gfx_scanout_dac.sv
@@ -0,0 +1,116 @@
+`include "gfx/gfx_defs.sv"
+
+module gfx_scanout_dac
+(
+ input logic clk,
+ rst_n,
+
+ input logic enable_clear,
+ input rgb24 clear_color,
+
+ input logic mask_fifo_out,
+ input mem_word fb_fifo_out,
+ input logic in_valid,
+ output logic in_ready,
+
+ input logic scan_ready,
+ output logic scan_valid,
+ scan_endofpacket,
+ scan_startofpacket,
+ output rgb30 scan_data,
+
+ output logic vsync
+);
+
+ logic dac_valid, half, half_mask, stall, endofpacket, startofpacket;
+ rgb24 pixel;
+ rgb32 fifo_pixel;
+ mem_word msw, lsw;
+ half_coord next_addr;
+ linear_coord max_addr, pixel_addr;
+
+ struct packed
+ {
+ logic endofpacket,
+ startofpacket;
+ rgb30 pixel;
+ } skid_in, skid_out;
+
+ assign scan_data = skid_out.pixel;
+ assign scan_endofpacket = skid_out.endofpacket;
+ assign scan_startofpacket = skid_out.startofpacket;
+
+ assign max_addr = `GFX_X_RES * `GFX_Y_RES - 1;
+
+ function color10 dac_color(color8 in);
+ dac_color = {in, {2{in[0]}}};
+ endfunction
+
+ assign fifo_pixel = {msw, lsw};
+ assign skid_in.pixel.r = dac_color(pixel.r);
+ assign skid_in.pixel.g = dac_color(pixel.g);
+ assign skid_in.pixel.b = dac_color(pixel.b);
+ assign skid_in.endofpacket = endofpacket;
+ assign skid_in.startofpacket = startofpacket;
+
+ always_comb begin
+ // Descarta fifo_pixel.a
+ pixel.r = fifo_pixel.r;
+ pixel.g = fifo_pixel.g;
+ pixel.b = fifo_pixel.b;
+
+ if (!half_mask)
+ pixel = clear_color;
+ end
+
+ gfx_skid_flow flow
+ (
+ .in_valid(dac_valid),
+ .out_ready(scan_ready),
+ .out_valid(scan_valid),
+ .*
+ );
+
+ gfx_skid_buf #(.WIDTH($bits(skid_in))) skid
+ (
+ .in(skid_in),
+ .out(skid_out),
+ .*
+ );
+
+ always_ff @(posedge clk or negedge rst_n)
+ if (!rst_n) begin
+ half <= 0;
+ vsync <= 0;
+ dac_valid <= 0;
+ pixel_addr <= 0;
+ end else begin
+ vsync <= 0;
+ if (in_ready && dac_valid) begin
+ vsync <= scan_endofpacket;
+ dac_valid <= 0;
+ end
+
+ if (in_ready && in_valid) begin
+ half <= !half;
+ dac_valid <= half;
+
+ if (half) begin
+ pixel_addr <= pixel_addr + 1;
+ if (pixel_addr == max_addr)
+ pixel_addr <= 0;
+ end
+ end
+ end
+
+ always_ff @(posedge clk)
+ if (in_ready && in_valid) begin
+ lsw <= msw;
+ msw <= fb_fifo_out;
+ half_mask <= mask_fifo_out;
+
+ endofpacket <= pixel_addr == max_addr;
+ startofpacket <= pixel_addr == 0;
+ end
+
+endmodule