Jelajahi Sumber

cores/hub75: Add support for the FM6126 init sequence

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 5 tahun lalu
induk
melakukan
b94f88978d

+ 4 - 0
cores/hub75/core.mk

@@ -10,6 +10,7 @@ RTL_SRCS_hub75 := $(addprefix rtl/, \
 	hub75_fb_writein.v \
 	hub75_framebuffer.v \
 	hub75_gamma.v \
+	hub75_init_inject.v \
 	hub75_linebuffer.v \
 	hub75_phy.v \
 	hub75_phy_ddr.v \
@@ -18,6 +19,9 @@ RTL_SRCS_hub75 := $(addprefix rtl/, \
 	hub75_top.v \
 )
 
+TESTBENCHES_hub75 := \
+	hub75_init_inject_tb \
+
 PREREQ_hub75 := \
 	$(BUILD_TMP)/gamma_table.hex
 

+ 203 - 0
cores/hub75/rtl/hub75_init_inject.v

@@ -0,0 +1,203 @@
+/*
+ * hub75_init_inject.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2019  Sylvain Munaut <tnt@246tNt.com>
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+`default_nettype none
+
+module hub75_init_inject #(
+	parameter integer N_BANKS  = 2,
+	parameter integer N_ROWS   = 32,
+	parameter integer N_COLS   = 64,
+	parameter integer N_CHANS  = 3,
+
+	parameter INIT_R1 = 16'h7FFF,
+	parameter INIT_R2 = 16'h0040,
+
+	// Auto-set
+	parameter integer SDW         = N_BANKS * N_CHANS,
+	parameter integer LOG_N_ROWS  = $clog2(N_ROWS),
+	parameter integer LOG_N_COLS  = $clog2(N_COLS)
+)(
+	// PHY interface signals input
+	input  wire phy_in_addr_inc,
+	input  wire phy_in_addr_rst,
+	input  wire [LOG_N_ROWS-1:0] phy_in_addr,
+	input  wire [SDW-1:0] phy_in_data,
+	input  wire phy_in_clk,
+	input  wire phy_in_le,
+	input  wire phy_in_blank,
+
+	// PHY interface signals input
+	output reg  phy_out_addr_inc,
+	output reg  phy_out_addr_rst,
+	output reg  [LOG_N_ROWS-1:0] phy_out_addr,
+	output reg  [SDW-1:0] phy_out_data,
+	output reg  phy_out_clk,
+	output reg  phy_out_le,
+	output reg  phy_out_blank,
+
+	// Control
+	input  wire init_req,
+
+	input  wire scan_go_in,
+
+	input  wire bcm_rdy_in,
+	output wire bcm_rdy_out,
+
+	input  wire shift_rdy_in,
+	input  wire blank_rdy_in,
+
+	// Clock / Reset
+	input  wire clk,
+	input  wire rst
+);
+
+	// Signals
+	// -------
+
+	// FSM
+	localparam
+		ST_IDLE		= 0,	// Idle
+		ST_WAIT		= 1,	// Wait for all activity to be done so we can use the PHY
+		ST_SHIFT	= 2,	// Shift the init
+		ST_GO		= 3;	// Issue go to scan block
+
+	reg [1:0] fsm_state;
+	reg [1:0] fsm_state_next;
+
+	// Request
+	reg init_done;
+
+	// Injection
+	wire active;
+	wire inject_data;
+	wire inject_le;
+
+	// Shift logic
+	reg  [LOG_N_COLS:0] col_cnt;
+	reg  col_last;
+	reg  col_le;
+	wire col_rst;
+
+	reg  reg_sel;
+	(* keep="true" *) wire [1:0] reg_bit;
+
+
+	// FSM
+	// ---
+
+	// State register
+	always @(posedge clk or posedge rst)
+		if (rst)
+			fsm_state <= ST_IDLE;
+		else
+			fsm_state <= fsm_state_next;
+
+	// Next-State logic
+	always @(*)
+	begin
+		// Default is to not move
+		fsm_state_next = fsm_state;
+
+		// Transistions ?
+		case (fsm_state)
+			ST_IDLE:
+				if (scan_go_in & ~init_done)
+					fsm_state_next = ST_WAIT;
+
+			ST_WAIT:
+				if (bcm_rdy_in & shift_rdy_in & blank_rdy_in)
+					fsm_state_next = ST_SHIFT;
+
+			ST_SHIFT:
+				if (col_last & reg_sel)
+					fsm_state_next = ST_GO;
+
+			ST_GO:
+				fsm_state_next = ST_IDLE;
+		endcase
+	end
+
+
+	// Control
+	// -------
+
+	always @(posedge clk)
+		if (rst)
+			init_done <= 1'b0;
+		else
+			init_done <= (init_done | (fsm_state == ST_GO)) & ~init_req;
+
+	// External handshake
+	assign bcm_rdy_out = (fsm_state == ST_IDLE) & bcm_rdy_in;
+
+
+	// Init sequence shift
+	// -------------------
+
+	// Flag
+	assign active = (fsm_state == ST_SHIFT);
+
+	// Column counter
+	assign col_rst = col_last | (fsm_state != ST_SHIFT);
+
+	always @(posedge clk)
+		if (col_rst) begin
+			col_cnt  <= N_COLS - 17;
+			col_last <= 1'b0;
+			col_le   <= 1'b0;
+		end else begin
+			col_cnt  <= col_cnt - 1;
+			col_last <= col_cnt[LOG_N_COLS] & (col_cnt[3:0] == 4'h1);
+			col_le   <= col_cnt[LOG_N_COLS] & (col_cnt[3:0] < (reg_sel ? 4'hd : 4'hc));
+		end
+	
+	// Reg select
+	always @(posedge clk)	
+		reg_sel <= (fsm_state == ST_SHIFT) ? (reg_sel ^ col_last) : 1'b0;
+
+	// ROM
+	assign reg_bit[0] = INIT_R1[col_cnt[3:0]];
+	assign reg_bit[1] = INIT_R2[col_cnt[3:0]];
+
+	// Outputs
+	assign inject_data = reg_bit[reg_sel];
+	assign inject_le   = col_le;
+
+
+	// PHY signal injection
+	// --------------------
+
+	always @(posedge clk)
+	begin
+		phy_out_addr_inc <= ~active ? phy_in_addr_inc : 1'b0;
+		phy_out_addr_rst <= ~active ? phy_in_addr_rst : 1'b0;
+		phy_out_addr     <= ~active ? phy_in_addr     : { LOG_N_ROWS{1'b0} };
+		phy_out_data     <= ~active ? phy_in_data     : { SDW{inject_data} };
+		phy_out_clk      <= ~active ? phy_in_clk      : 1'b1;
+		phy_out_le       <= ~active ? phy_in_le       : inject_le;
+		phy_out_blank    <= ~active ? phy_in_blank    : 1'b1;
+	end
+
+endmodule

+ 76 - 16
cores/hub75/rtl/hub75_top.v

@@ -35,6 +35,7 @@ module hub75_top #(
 	parameter integer PHY_DDR  = 0,		// PHY DDR data output
 	parameter integer PHY_AIR  = 0,		// PHY Address Inc/Reset
 
+	parameter PANEL_INIT = "NONE",		// 'NONE' or 'FM6126'
 	parameter SCAN_MODE = "ZIGZAG",		// 'LINEAR' or 'ZIGZAG'
 
 	// Auto-set
@@ -100,6 +101,14 @@ module hub75_top #(
 	wire phy_le;
 	wire phy_blank;
 
+	wire phz_addr_inc;
+	wire phz_addr_rst;
+	wire [LOG_N_ROWS-1:0] phz_addr;
+	wire [SDW-1:0] phz_data;
+	wire phz_clk;
+	wire phz_le;
+	wire phz_blank;
+
 	// Frame Buffer access
 		// Read - Back Buffer loading
 	wire [LOG_N_ROWS-1:0] fbr_row_addr;
@@ -120,7 +129,7 @@ module hub75_top #(
 	wire [LOG_N_ROWS-1:0] bcm_row;
 	wire bcm_row_first;
 	wire bcm_go;
-	wire bcm_rdy;
+	wire bcm_rdy, bcm_rdz;
 
 	// Shifter
 	wire [N_PLANES-1:0] shift_plane;
@@ -194,7 +203,7 @@ module hub75_top #(
 		.bcm_row(bcm_row),				// -> hub75_bcm
 		.bcm_row_first(bcm_row_first),	// -> hub75_bcm
 		.bcm_go(bcm_go),				// -> hub75_bcm
-		.bcm_rdy(bcm_rdy),				// <- hub75_bcm
+		.bcm_rdy(bcm_rdz),				// <- hub75_bcm
 		.fb_row_addr(fbr_row_addr),		// -> hub75_framebuffer
 		.fb_row_load(fbr_row_load),		// -> hub75_framebuffer
 		.fb_row_rdy(fbr_row_rdy),		// <- hub75_framebuffer
@@ -262,6 +271,57 @@ module hub75_top #(
 		.rst(rst)						// <- top
 	);
 
+	// Init injector
+	generate
+		if (PANEL_INIT == "NONE") begin
+
+			// Direct PHY connection
+			assign phz_addr_inc	= phy_addr_inc;
+			assign phz_addr_rst	= phy_addr_rst;
+			assign phz_addr		= phy_addr;
+			assign phz_data		= phy_data;
+			assign phz_clk		= phy_clk;
+			assign phz_le		= phy_le;
+			assign phz_blank	= phy_blank;
+
+			// No gating
+			assign bcm_rdz = bcm_rdy;
+
+		end else begin
+
+			hub75_init_inject #(
+				.N_BANKS(N_BANKS),
+				.N_ROWS(N_ROWS),
+				.N_COLS(N_COLS),
+				.N_CHANS(N_CHANS)
+			) init_I (
+				.phy_in_addr_inc(phy_addr_inc),
+				.phy_in_addr_rst(phy_addr_rst),
+				.phy_in_addr(phy_addr),
+				.phy_in_data(phy_data),
+				.phy_in_clk(phy_clk),
+				.phy_in_le(phy_le),
+				.phy_in_blank(phy_blank),
+				.phy_out_addr_inc(phz_addr_inc),
+				.phy_out_addr_rst(phz_addr_rst),
+				.phy_out_addr(phz_addr),
+				.phy_out_data(phz_data),
+				.phy_out_clk(phz_clk),
+				.phy_out_le(phz_le),
+				.phy_out_blank(phz_blank),
+				.init_req(1'b1),
+				.scan_go_in(scan_go),
+				.bcm_rdy_in(bcm_rdy),
+				.bcm_rdy_out(bcm_rdz),
+				.shift_rdy_in(shift_rdy),
+				.blank_rdy_in(blank_rdy),
+				.clk(clk),
+				.rst(rst)
+			);
+
+		end
+	endgenerate
+
 	// Physical layer control
 	generate
 		if (PHY_DDR == 0)
@@ -278,13 +338,13 @@ module hub75_top #(
 				.hub75_clk(hub75_clk),			// -> pad
 				.hub75_le(hub75_le),			// -> pad
 				.hub75_blank(hub75_blank),		// -> pad
-				.phy_addr_inc(phy_addr_inc),	// <- hub75_bcm
-				.phy_addr_rst(phy_addr_rst),	// <- hub75_bcm
-				.phy_addr(phy_addr),			// <- hub75_bcm
-				.phy_data(phy_data),			// <- hub75_shift
-				.phy_clk(phy_clk),				// <- hub75_shift
-				.phy_le(phy_le),				// <- hub75_bcm
-				.phy_blank(phy_blank),			// <- hub75_blanking
+				.phy_addr_inc(phz_addr_inc),	// <- hub75_bcm
+				.phy_addr_rst(phz_addr_rst),	// <- hub75_bcm
+				.phy_addr(phz_addr),			// <- hub75_bcm
+				.phy_data(phz_data),			// <- hub75_shift
+				.phy_clk(phz_clk),				// <- hub75_shift
+				.phy_le(phz_le),				// <- hub75_bcm
+				.phy_blank(phz_blank),			// <- hub75_blanking
 				.clk(clk),						// <- top
 				.rst(rst)						// <- top
 			);
@@ -303,13 +363,13 @@ module hub75_top #(
 				.hub75_clk(hub75_clk),			// -> pad
 				.hub75_le(hub75_le),			// -> pad
 				.hub75_blank(hub75_blank),		// -> pad
-				.phy_addr_inc(phy_addr_inc),	// <- hub75_bcm
-				.phy_addr_rst(phy_addr_rst),	// <- hub75_bcm
-				.phy_addr(phy_addr),			// <- hub75_bcm
-				.phy_data(phy_data),			// <- hub75_shift
-				.phy_clk(phy_clk),				// <- hub75_shift
-				.phy_le(phy_le),				// <- hub75_bcm
-				.phy_blank(phy_blank),			// <- hub75_blanking
+				.phy_addr_inc(phz_addr_inc),	// <- hub75_bcm
+				.phy_addr_rst(phz_addr_rst),	// <- hub75_bcm
+				.phy_addr(phz_addr),			// <- hub75_bcm
+				.phy_data(phz_data),			// <- hub75_shift
+				.phy_clk(phz_clk),				// <- hub75_shift
+				.phy_le(phz_le),				// <- hub75_bcm
+				.phy_blank(phz_blank),			// <- hub75_blanking
 				.clk(clk),						// <- top
 				.clk_2x(clk_2x),				// <- top
 				.rst(rst)						// <- top

+ 107 - 0
cores/hub75/sim/hub75_init_inject_tb.v

@@ -0,0 +1,107 @@
+/*
+ * hub75_init_inject_tb.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2019  Sylvain Munaut <tnt@246tNt.com>
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+`default_nettype none
+`timescale 1ns / 100ps
+
+module hub75_init_inject_tb;
+
+	// Signals
+	reg rst = 1;
+	reg clk = 1;
+
+	wire phy_out_addr_inc;
+	wire phy_out_addr_rst;
+	wire [4:0] phy_out_addr;
+	wire [5:0] phy_out_data;
+	wire phy_out_clk;
+	wire phy_out_le;
+	wire phy_out_blank;
+
+	wire scan_go_in;
+	wire scan_go_out;
+	wire scan_rdy_in;
+	wire scan_rdy_out;
+	wire bcm_rdy_in;
+
+	// Setup recording
+	initial begin
+		$dumpfile("hub75_init_inject_tb.vcd");
+		$dumpvars(0,hub75_init_inject_tb);
+	end
+
+	// Reset pulse
+	initial begin
+		# 31 rst = 0;
+		# 20000 $finish;
+	end
+
+	// Clocks
+	always #5 clk = !clk;
+
+	// DUT
+	hub75_init_inject dut_I (
+		.phy_in_addr_inc(1'b0),
+		.phy_in_addr_rst(1'b0),
+		.phy_in_addr(5'h00),
+		.phy_in_data(6'h00),
+		.phy_in_clk(1'b0),
+		.phy_in_le(1'b0),
+		.phy_in_blank(1'b1),
+		.phy_out_addr_inc(phy_out_addr_inc),
+		.phy_out_addr_rst(phy_out_addr_rst),
+		.phy_out_addr(phy_out_addr),
+		.phy_out_data(phy_out_data),
+		.phy_out_clk(phy_out_clk),
+		.phy_out_le(phy_out_le),
+		.phy_out_blank(phy_out_blank),
+		.scan_go_in(scan_go_in),
+		.scan_go_out(scan_go_out),
+		.scan_rdy_in(scan_rdy_in),
+		.scan_rdy_out(scan_rdy_out),
+		.bcm_rdy_in(bcm_rdy_in),
+		.clk(clk),
+		.rst(rst)
+	);
+
+	// Dummy
+	assign scan_go_in = scan_rdy_out;
+	assign scan_rdy_in = 1'b1;
+	assign bcm_rdy_in = 1'b1;
+
+	// PHY
+	hub75_phy phy_I (
+		.phy_addr_inc(phy_out_addr_inc),
+		.phy_addr_rst(phy_out_addr_rst),
+		.phy_addr(phy_out_addr),
+		.phy_data(phy_out_data),
+		.phy_clk(phy_out_clk),
+		.phy_le(phy_out_le),
+		.phy_blank(phy_out_blank),
+		.clk(clk),
+		.rst(rst)
+	);
+
+endmodule