summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2022-11-13 22:37:10 -0600
committerAlejandro Soto <alejandro@34project.org>2022-11-14 21:09:19 -0600
commitcad870295dfb741d5c24c25016c5bba878bc37e5 (patch)
treee69b125a0f9008fe347abba6b4e0e5d5695dd573
parentbf746b3ee73081dff0da03a54749d1fd3c0215f6 (diff)
Implement VGA controller
-rw-r--r--platform.qsys18
-rw-r--r--rtl/vga.sv182
-rw-r--r--vga_controller_hw.tcl7
3 files changed, 179 insertions, 28 deletions
diff --git a/platform.qsys b/platform.qsys
index 51c1d54..9ccd6ed 100644
--- a/platform.qsys
+++ b/platform.qsys
@@ -875,7 +875,7 @@
<parameter name="gui_fractional_cout" value="32" />
<parameter name="gui_mif_generate" value="false" />
<parameter name="gui_multiply_factor" value="1" />
- <parameter name="gui_number_of_clocks" value="4" />
+ <parameter name="gui_number_of_clocks" value="5" />
<parameter name="gui_operation_mode" value="direct" />
<parameter name="gui_output_clock_frequency0" value="50.0" />
<parameter name="gui_output_clock_frequency1" value="100.0" />
@@ -887,9 +887,9 @@
<parameter name="gui_output_clock_frequency15" value="100.0" />
<parameter name="gui_output_clock_frequency16" value="100.0" />
<parameter name="gui_output_clock_frequency17" value="100.0" />
- <parameter name="gui_output_clock_frequency2" value="25.175" />
- <parameter name="gui_output_clock_frequency3" value="25.175" />
- <parameter name="gui_output_clock_frequency4" value="100.0" />
+ <parameter name="gui_output_clock_frequency2" value="143.0" />
+ <parameter name="gui_output_clock_frequency3" value="143.0" />
+ <parameter name="gui_output_clock_frequency4" value="25.175" />
<parameter name="gui_output_clock_frequency5" value="100.0" />
<parameter name="gui_output_clock_frequency6" value="100.0" />
<parameter name="gui_output_clock_frequency7" value="100.0" />
@@ -924,7 +924,7 @@
<parameter name="gui_phase_shift_deg16" value="0.0" />
<parameter name="gui_phase_shift_deg17" value="0.0" />
<parameter name="gui_phase_shift_deg2" value="0.0" />
- <parameter name="gui_phase_shift_deg3" value="0.0" />
+ <parameter name="gui_phase_shift_deg3" value="-50.0" />
<parameter name="gui_phase_shift_deg4" value="0.0" />
<parameter name="gui_phase_shift_deg5" value="0.0" />
<parameter name="gui_phase_shift_deg6" value="0.0" />
@@ -947,7 +947,7 @@
<parameter name="gui_ps_units16" value="ps" />
<parameter name="gui_ps_units17" value="ps" />
<parameter name="gui_ps_units2" value="ps" />
- <parameter name="gui_ps_units3" value="ps" />
+ <parameter name="gui_ps_units3" value="degrees" />
<parameter name="gui_ps_units4" value="ps" />
<parameter name="gui_ps_units5" value="ps" />
<parameter name="gui_ps_units6" value="ps" />
@@ -959,7 +959,7 @@
<parameter name="gui_reference_clock_frequency" value="50.0" />
<parameter name="gui_switchover_delay" value="0" />
<parameter name="gui_switchover_mode">Automatic Switchover</parameter>
- <parameter name="gui_use_locked" value="true" />
+ <parameter name="gui_use_locked" value="false" />
</module>
<module name="timer_0" kind="altera_avalon_timer" version="20.1" enabled="1">
<parameter name="alwaysRun" value="false" />
@@ -990,7 +990,7 @@
<parameter name="TRP" value="15.0" />
<parameter name="TWR" value="14.0" />
<parameter name="casLatency" value="3" />
- <parameter name="clockRate" value="25396825" />
+ <parameter name="clockRate" value="142857142" />
<parameter name="columnWidth" value="10" />
<parameter name="componentName" value="$${FILENAME}_vram" />
<parameter name="dataWidth" value="16" />
@@ -1097,7 +1097,7 @@
<connection
kind="clock"
version="20.1"
- start="pll_0.outclk2"
+ start="pll_0.outclk4"
end="vga_controller_0.clock_sink" />
<connection
kind="reset"
diff --git a/rtl/vga.sv b/rtl/vga.sv
index 4ff0ad1..d720578 100644
--- a/rtl/vga.sv
+++ b/rtl/vga.sv
@@ -2,15 +2,52 @@
`define VGA_PIXCLK_HZ 25_175_000
+/* Todas las constantes mágicas están en el DE1-SoC CD-ROM bajo
+ * Datasheet/SDRAM/IS42R16320D.pdf
+ *
+ * Este módulo es el CRTC de VGA. Consideraciones:
+ *
+ * - Se necesita una resolución clásica de 640x480 @ ~60Hz.
+ *
+ * - La VRAM en este caso es una unidad de memoria dinámica real, externa a la
+ * fábrica de la FPGA, que tiene todas las propiedades esperables de SDRAM
+ * real, como cálculos de temporización e impedancias, latencia de más de un
+ * ciclo, strobe, etc.
+ *
+ * - Este módulo debe generar una señal para el DAC que no puede detenerse,
+ * sino que debe emitir exactamente un píxel por ciclo de pixclk, excepto
+ * fuera de las regiones activas horizontal y vertical.
+ *
+ * - La VRAM tiene 4 bancos de 10 columnas y 13 filas con celdas de 16 bits.
+ *
+ * - El CPU puede escribir a VRAM al mismo tiempo que el CRTC lee, ambos son
+ * maestros de un mismo esclavo.
+ *
+ * Por lo tanto:
+ *
+ * - El formato de framebuffer es row-major r5g6b5.
+ *
+ * - Existen dos buffers de scanline, uno para filas pares y otro para
+ * impares.
+ *
+ * - Mientras el CRTC muestra una scanline en pantalla, el maestro Avalon lee
+ * la siguiente de VRAM usando pipelining para lograr esto último lo más
+ * rápido que la VRAM sea capaz. Según el *_hw.tcl del IP para VRAM, este
+ * soporta hasta siete transacciones en pipeline. Ocurrirán glitches si VRAM
+ * no es capaz de producir una scanline antes de que el CRTC la necesite.
+ */
+
module vga
(
input logic clk,
- input logic rst_n,
+ rst_n,
+ // 26 bits direccionan 64MiB
output logic[25:0] avl_address,
output logic avl_read,
- input logic[31:0] avl_readdata,
+ input logic[15:0] avl_readdata,
input logic avl_waitrequest,
+ avl_readdatavalid,
output logic vga_clk,
vga_hsync,
@@ -22,24 +59,137 @@ module vga
vga_b
);
- localparam H_ACTIVE = `COORD_BITS'd640;
- localparam H_FPORCH = `COORD_BITS'd16;
- localparam H_SYNC = `COORD_BITS'd96;
- localparam H_BPORCH = `COORD_BITS'd48;
- localparam V_ACTIVE = `COORD_BITS'd480;
- localparam V_FPORCH = `COORD_BITS'd11;
- localparam V_SYNC = `COORD_BITS'd2;
- localparam V_BPORCH = `COORD_BITS'd31;
+ localparam H_ACTIVE_NO = 640;
+ localparam H_ACTIVE = `COORD_BITS'd640;
+ localparam H_FPORCH = `COORD_BITS'd16;
+ localparam H_SYNC = `COORD_BITS'd96;
+ localparam H_BPORCH = `COORD_BITS'd48;
+ localparam V_ACTIVE = `COORD_BITS'd480;
+ localparam V_FPORCH = `COORD_BITS'd11;
+ localparam V_SYNC = `COORD_BITS'd2;
+ localparam V_BPORCH = `COORD_BITS'd31;
+
+ localparam H_FPORCH_AT = H_BPORCH + H_ACTIVE;
+ localparam H_SYNC_AT = H_FPORCH_AT + H_FPORCH;
+ localparam H_TOTAL = H_SYNC_AT + H_SYNC;
+ localparam V_FPORCH_AT = V_BPORCH + V_ACTIVE;
+ localparam V_SYNC_AT = V_FPORCH_AT + V_FPORCH;
+ localparam V_TOTAL = V_SYNC_AT + V_SYNC;
+
+ typedef struct packed
+ {
+ logic[4:0] r;
+ logic[5:0] g;
+ logic[4:0] b;
+ } pix;
- localparam H_SYNC_AT = H_BPORCH + H_ACTIVE + H_FPORCH;
- localparam H_TOTAL = H_SYNC_AT + H_SYNC;
- localparam V_SYNC_AT = V_BPORCH + V_ACTIVE + V_FPORCH;
- localparam V_TOTAL = V_SYNC_AT + V_SYNC;
+ enum int unsigned
+ {
+ A,
+ B
+ } reading, next_reading, fill_start_reading;
- logic[7:0] r, g, b;
+ pix current, read_a, read_b;
+ pix scanline_a[H_ACTIVE_NO];
+ pix scanline_b[H_ACTIVE_NO];
+
+ logic next_active;
+ logic[24:0] addr;
+ logic[`COORD_BITS - 1:0] x, y, next_x, next_y, next_hsync,
+ pending_read, pending_data, read_idx, write_idx;
assign vga_clk = clk;
- assign vga_blank_n = 1;
assign vga_sync_n = 0;
+ assign vga_blank_n = 1;
+ assign avl_address = {addr, 1'b0};
+
+ assign vga_r = {current.r, current.r[4], current.r[4], current.r[4]};
+ assign vga_g = {current.g, current.g[5], current.g[5]};
+ assign vga_b = {current.b, current.b[4], current.b[4], current.b[4]};
+
+ assign read_idx = next_x - H_BPORCH;
+ assign next_reading = next_y[0] ^ (next_x < H_FPORCH_AT) ? A : B;
+
+ assign next_active
+ = next_x >= H_BPORCH && next_x < H_FPORCH_AT
+ && next_y >= V_BPORCH && next_y < V_FPORCH_AT;
+
+ always_comb begin
+ unique case(reading)
+ A: current = read_a;
+ B: current = read_b;
+ endcase
+
+ if(x != H_TOTAL - 1) begin
+ next_x = x + 1;
+ next_y = y;
+ end else begin
+ next_x = 0;
+ next_y = y != V_TOTAL - 1 ? y + 1 : 0;
+ end
+ end
+
+ always @(posedge clk or negedge rst_n)
+ if(!rst_n) begin
+ x <= H_TOTAL - 1;
+ y <= V_TOTAL - 1;
+ reading <= A;
+ write_idx <= 0;
+ pending_read <= 0;
+ pending_data <= 0;
+ fill_start_reading <= A;
+
+ read_a <= 0;
+ read_b <= 0;
+
+ addr <= 0;
+ avl_read <= 0;
+
+ vga_hsync <= 0;
+ vga_vsync <= 0;
+ end else begin
+ if(next_active)
+ unique case(next_reading)
+ A: read_a <= scanline_a[read_idx];
+ B: read_b <= scanline_b[read_idx];
+ endcase
+
+ if(avl_readdatavalid) begin
+ unique case(fill_start_reading)
+ A: scanline_b[write_idx] <= avl_readdata;
+ B: scanline_a[write_idx] <= avl_readdata;
+ endcase
+
+ write_idx <= write_idx + 1;
+ pending_data <= pending_data - 1;
+ end
+
+ if(!avl_read || !avl_waitrequest) begin
+ avl_read <= 0;
+
+ if(pending_read != 0) begin
+ addr <= addr + 1;
+ avl_read <= 1;
+ pending_read <= pending_read - 1;
+ end
+ end
+
+ if(pending_read == 0 && pending_data == 0 && next_reading != reading) begin
+ if(y >= V_BPORCH - 2 && y < V_FPORCH_AT - 2) begin
+ write_idx <= 0;
+ pending_read <= H_ACTIVE;
+ pending_data <= H_ACTIVE;
+ fill_start_reading <= next_reading;
+ end else
+ addr <= {$bits(addr){1'b1}};
+ end
+
+ x <= next_x;
+ y <= next_y;
+ reading <= next_reading;
+
+ vga_hsync <= next_x < H_SYNC_AT;
+ vga_vsync <= next_y < V_SYNC_AT;
+ end
endmodule
diff --git a/vga_controller_hw.tcl b/vga_controller_hw.tcl
index 2816d9c..bf95f0a 100644
--- a/vga_controller_hw.tcl
+++ b/vga_controller_hw.tcl
@@ -1,11 +1,11 @@
# TCL File Generated by Component Editor 20.1
-# Thu Nov 03 11:42:18 CST 2022
+# Mon Nov 14 02:57:50 GMT 2022
# DO NOT MODIFY
#
# vga_controller "vga_controller" v1.0
-# Alejandro Soto 2022.11.03.11:42:18
+# Alejandro Soto 2022.11.14.02:57:50
#
#
@@ -110,8 +110,9 @@ set_interface_property avalon_master SVD_ADDRESS_GROUP ""
add_interface_port avalon_master avl_address address Output 26
add_interface_port avalon_master avl_read read Output 1
-add_interface_port avalon_master avl_readdata readdata Input 32
+add_interface_port avalon_master avl_readdata readdata Input 16
add_interface_port avalon_master avl_waitrequest waitrequest Input 1
+add_interface_port avalon_master avl_readdatavalid readdatavalid Input 1
#