From f651a31f0484de03bc08674262e9d1a6f362fe16 Mon Sep 17 00:00:00 2001 From: Luca Date: Sat, 19 Oct 2024 00:01:38 +0200 Subject: [PATCH] feat: add display driver --- Makefile | 2 +- arty_a7_35t.xdc | 16 +++++ dvi.v | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ pixelflut.v | 20 +++++++ 4 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 dvi.v diff --git a/Makefile b/Makefile index 647eb2e..4a50b32 100644 --- a/Makefile +++ b/Makefile @@ -22,5 +22,5 @@ pixelflut.frames: pixelflut.fasm pixelflut.fasm: arty_a7_35t.xdc pixelflut.json nextpnr-xilinx --chipdb "$(CHIPDB_DIR)/$(PART).bin" --fasm $@ --json pixelflut.json --xdc arty_a7_35t.xdc -pixelflut.json: pixelflut.v +pixelflut.json: pixelflut.v dvi.v yosys -q -p 'synth_xilinx -top pixelflut; write_json $@' $^ diff --git a/arty_a7_35t.xdc b/arty_a7_35t.xdc index b4e5ff4..24a5548 100644 --- a/arty_a7_35t.xdc +++ b/arty_a7_35t.xdc @@ -41,23 +41,39 @@ set_property -dict { PACKAGE_PIN G6 IOSTANDARD LVCMOS33 } [get_ports led0_r] ## Pmod Header JA #set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports ja[0]] #IO_0_15 Sch=ja[1] +set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports dvi_d[11]] #set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports ja[1]] #IO_L4P_T0_15 Sch=ja[2] +set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports dvi_d[9]] #set_property -dict { PACKAGE_PIN A11 IOSTANDARD LVCMOS33 } [get_ports ja[2]] #IO_L4N_T0_15 Sch=ja[3] +set_property -dict { PACKAGE_PIN A11 IOSTANDARD LVCMOS33 } [get_ports dvi_d[7]] #set_property -dict { PACKAGE_PIN D12 IOSTANDARD LVCMOS33 } [get_ports ja[3]] #IO_L6P_T0_15 Sch=ja[4] +set_property -dict { PACKAGE_PIN D12 IOSTANDARD LVCMOS33 } [get_ports dvi_d[5]] #set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 } [get_ports ja[4]] #IO_L6N_T0_VREF_15 Sch=ja[7] +set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 } [get_ports dvi_d[10]] #set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports ja[5]] #IO_L10P_T1_AD11P_15 Sch=ja[8] +set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports dvi_d[8]] #set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports ja[6]] #IO_L10N_T1_AD11N_15 Sch=ja[9] +set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports dvi_d[6]] #set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports ja[7]] #IO_25_15 Sch=ja[10] +set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports dvi_d[4]] ## Pmod Header JB #set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 } [get_ports jb[0]] #IO_L11P_T1_SRCC_15 Sch=jb_p[1] +set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 } [get_ports dvi_d[3]] #set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports jb[1]] #IO_L11N_T1_SRCC_15 Sch=jb_n[1] +set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports dvi_d[1]] #set_property -dict { PACKAGE_PIN D15 IOSTANDARD LVCMOS33 } [get_ports jb[2]] #IO_L12P_T1_MRCC_15 Sch=jb_p[2] +set_property -dict { PACKAGE_PIN D15 IOSTANDARD LVCMOS33 } [get_ports dvi_ck] #set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports jb[3]] #IO_L12N_T1_MRCC_15 Sch=jb_n[2] +set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports dvi_hs] #set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports jb[4]] #IO_L23P_T3_FOE_B_15 Sch=jb_p[3] +set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports dvi_d[2]] #set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports jb[5]] #IO_L23N_T3_FWE_B_15 Sch=jb_n[3] +set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports dvi_d[0]] #set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports jb[6]] #IO_L24P_T3_RS1_15 Sch=jb_p[4] +set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports dvi_de] #set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports jb[7]] #IO_L24N_T3_RS0_15 Sch=jb_n[4] +set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports dvi_vs] ## Pmod Header JC #set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports jc[0]] #IO_L20P_T3_A08_D24_14 Sch=jc_p[1] diff --git a/dvi.v b/dvi.v new file mode 100644 index 0000000..8edb563 --- /dev/null +++ b/dvi.v @@ -0,0 +1,154 @@ +module dvi #( + parameter [15:0] BASE_ADDR = 16'h0000, + + parameter H_ACTIVE_START = 136, + parameter H_BLANK_START = 792, + parameter H_DATA_START = 306, + parameter H_DATA_END = 622, + parameter H_SYNC_ACTIVE = 0, + parameter H_SYNC_TIME = 96, + parameter H_TOTAL = 800, + + parameter V_ACTIVE_START = 27, + parameter V_BLANK_START = 523, + parameter V_DATA_START = 157, + parameter V_DATA_END = 394, + parameter V_SYNC_ACTIVE = 0, + parameter V_SYNC_TIME = 2, + parameter V_TOTAL = 525 +) ( + input bus_clk, + input [15:0] bus_data, + output reg [15:0] bus_addr, + + input reset, + + output reg [11:0] d, + output reg ck, + output reg de, + output hs, + output vs +); + localparam OUTPUT_IDLE = 2'b00; + localparam OUTPUT_BLANK = 2'b01; + localparam OUTPUT_DATA = 2'b10; + + localparam FETCH_LOW = 2'b00; + localparam FETCH_MID = 2'b01; + localparam FETCH_HIGH = 2'b10; + localparam FETCH_SWAP = 2'b11; + + reg [1:0] output_state; + reg fetch_en; + reg [1:0] fetch_state; + reg [47:0] data [1:0]; + reg active_data; + reg [11:0] x; + reg [10:0] y; + + assign hs = x < H_SYNC_TIME ? H_SYNC_ACTIVE : ~H_SYNC_ACTIVE; + assign vs = y < V_SYNC_TIME ? V_SYNC_ACTIVE : ~V_SYNC_ACTIVE; + + initial begin + bus_addr <= BASE_ADDR; + + d <= 12'b0; + ck <= 0; + de <= 0; + + output_state <= OUTPUT_IDLE; + fetch_en <= 0; + fetch_state <= FETCH_LOW; + data[0] <= 48'b0; + data[1] <= 48'b0; + active_data <= 0; + x <= 12'b0; + y <= 11'b0; + end + + always @(posedge bus_clk) begin + de <= 0; + + fetch_en <= 0; + + if (~ck) x <= x + 1; + + if (reset) output_state <= OUTPUT_IDLE; + + case (output_state) + OUTPUT_IDLE: begin + x <= 12'b0; + y <= 11'b0; + + if (~reset) begin + ck <= 0; + + output_state <= OUTPUT_BLANK; + end + end + OUTPUT_BLANK: begin + if (y >= V_ACTIVE_START && y < V_BLANK_START && x == H_ACTIVE_START-1) output_state <= OUTPUT_DATA; + + if (~ck && x == H_TOTAL-1) begin + x <= 12'b0; + y <= y + 1; + + if (y == V_TOTAL-1) begin + bus_addr <= BASE_ADDR; + + fetch_state <= FETCH_LOW; + y <= 11'b0; + end + end + end + OUTPUT_DATA: begin + d <= 12'b0; + de <= 1; + + if (y == V_DATA_START && (~ck && x == H_DATA_START-3 || x >= H_DATA_START-2) && x < H_DATA_START) fetch_en <= 1; + + if (y >= V_DATA_START && y < V_DATA_END && x >= H_DATA_START && x < H_DATA_END) begin + d <= data[active_data][11:0]; + + fetch_en <= 1; + end + + if (x == H_BLANK_START-1) begin + output_state <= OUTPUT_BLANK; + end + end + default: begin + output_state <= OUTPUT_IDLE; + end + endcase + end + + always @(negedge bus_clk) begin + if (output_state != OUTPUT_IDLE) ck <= ~ck; + + if (fetch_en) begin + data[active_data] <= {12'b0, data[active_data][47:12]}; + + if (fetch_state != FETCH_SWAP) bus_addr <= bus_addr + 1; + + case (fetch_state) + FETCH_LOW: begin + fetch_state <= FETCH_MID; + data[~active_data][15:0] <= bus_data; + end + FETCH_MID: begin + fetch_state <= FETCH_HIGH; + data[~active_data][31:16] <= bus_data; + end + FETCH_HIGH: begin + fetch_state <= FETCH_SWAP; + data[~active_data][47:32] <= bus_data; + end + FETCH_SWAP: begin + fetch_state <= FETCH_LOW; + active_data <= ~active_data; + end + endcase + end + end +endmodule diff --git a/pixelflut.v b/pixelflut.v index 1e517cb..886e8d7 100644 --- a/pixelflut.v +++ b/pixelflut.v @@ -4,6 +4,12 @@ module pixelflut ( output led0_r, output led0_g, output led0_b, + + output [11:0] dvi_d, + output dvi_ck, + output dvi_de, + output dvi_hs, + output dvi_vs, ); reg [31:0] ctr; reg [2:0] led0_state = 3'b0; @@ -20,4 +26,18 @@ module pixelflut ( ctr <= ctr + 1'b1; end end + + wire [15:0] dvi_bus; + + dvi display ( + .bus_clk (sys_clk), + .bus_data(dvi_bus), + .bus_addr(dvi_bus), + .reset (1'b0), + .d (dvi_d), + .ck (dvi_ck), + .de (dvi_de), + .hs (dvi_hs), + .vs (dvi_vs), + ); endmodule