dsi-shield/hdl/rtl/dsi_core/dsi_core.v

456 lines
12 KiB
Verilog

/*
* DSI Core
* Copyright (C) 2013 twl <twlostow@printf.cc>
*
* This library 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.0 of the License, or (at your option) any later version.
*
* This library 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 library.
*/
`include "dsi_defs.vh"
`timescale 1ns/1ps
/*
* dsi_core.v
*
* Top level of the core. Currently works on Spartan-6 FPGAs only.
*/
module dsi_core
(
// system clock (= pixel clock for the moment)
clk_sys_i,
// PHY serdes clock (= 8x pixel clock)
clk_phy_i,
rst_n_i,
pll_locked_i,
frame_ready_o,
// Pixel FIFO interface
fifo_full_o,
fifo_pixels_i,
fifo_frame_i,
fifo_wr_i,
// DSI high speed output (OSERDES2)
dsi_hs_p_o,
dsi_hs_n_o,
// DSI low power output
dsi_lp_p_o,
dsi_lp_n_o,
// Lop power output enable
dsi_lp_oe_o,
// DSI clock lane output
dsi_clk_p_o,
dsi_clk_n_o,
// DSI clock lane LP signals + output enable
dsi_clk_lp_p_o,
dsi_clk_lp_n_o,
dsi_clk_lp_oe_o,
// Displat reset pin
dsi_reset_n_o,
// Host control registers
host_a_i,
host_d_i,
host_d_o,
host_wr_i
);
parameter g_pixels_per_clock = 1;
parameter g_lanes = 3;
parameter g_fifo_size = 1024;
parameter g_invert_lanes = 0;
parameter g_invert_clock = 0;
localparam g_pixel_width = 24 * g_pixels_per_clock;
input clk_sys_i, clk_phy_i, rst_n_i;
input pll_locked_i;
output frame_ready_o;
output fifo_full_o;
input [g_pixel_width - 1 : 0 ] fifo_pixels_i;
input fifo_frame_i, fifo_wr_i;
output dsi_clk_p_o, dsi_clk_n_o;
output [g_lanes-1:0] dsi_hs_p_o, dsi_hs_n_o;
output [g_lanes-1:0] dsi_lp_p_o, dsi_lp_n_o;
output [g_lanes-1:0] dsi_lp_oe_o;
output dsi_clk_lp_p_o, dsi_clk_lp_n_o;
output dsi_clk_lp_oe_o;
output reg dsi_reset_n_o;
input [5:0] host_a_i;
input [31:0] host_d_i;
output [31:0] host_d_o;
input host_wr_i;
///////////////////
// PHY/Serdes layer
///////////////////
reg tick = 0;
wire [g_lanes-1:0] phy_hs_ready_lane, lp_ready_lane, lp_readback_lane;
wire [g_lanes:0] serdes_oe_lane;
wire [g_lanes-1:0] lp_txp, lp_txn, lp_oe;
wire lp_ready;
wire phy_hs_ready;
assign lp_ready = lp_ready_lane[0];
assign phy_hs_ready = phy_hs_ready_lane[0];
wire phy_hs_request;
wire [g_lanes * 8 -1 :0] phy_hs_data;
wire [g_lanes-1:0] phy_hs_valid;
wire [(g_lanes + 1) * 8 - 1: 0] serdes_data, serdes_data_interleaved;
reg r_dsi_clk_en = 0;
reg lp_request = 0;
reg lp_valid = 0;
reg [7:0] lp_data = 0;
generate
genvar i;
for(i=0;i<g_lanes;i=i+1)
begin
dphy_lane
#(
.g_invert(g_invert_lanes&(1<<i)?1:0)
)
U_LaneX
(
.clk_i(clk_sys_i),
.rst_n_i(rst_n_i),
.tick_i(tick),
.hs_request_i (phy_hs_request),
.hs_data_i (phy_hs_data[ (g_lanes-i) *8-1 -:8]), // >> ((g_lanes - 1 - i)*8)}),
.hs_ready_o (phy_hs_ready_lane[i]),
.hs_valid_i(phy_hs_valid[i]),
.lp_request_i (lp_request),
.lp_data_i (lp_data),
.lp_valid_i (i ==0 ? lp_valid : 1'b0),
.lp_ready_o (lp_ready_lane[i]),
.serdes_data_o(serdes_data[i*8+:8]),
.lp_txp_o(lp_txp[i]),
.lp_txn_o(lp_txn[i]),
.lp_oe_o(lp_oe[i])
);
assign dsi_lp_p_o[i] = lp_txp[i];
assign dsi_lp_n_o[i] = lp_txn[i];
assign dsi_lp_oe_o[i] = lp_oe[0];
assign serdes_oe_lane[i] = ~lp_oe[0];
end // for (i=0;i<g_lanes;i=i+1)
endgenerate
wire clk_lane_ready;
wire dsi_clk_lp_oe;
dphy_lane
#(
.g_invert(g_invert_clock)
) U_ClockLane (
.clk_i(clk_sys_i),
.rst_n_i(rst_n_i),
.tick_i(tick),
.hs_request_i (r_dsi_clk_en),
.hs_data_i (clk_lane_ready ? 8'haa : 8'h00),
.hs_ready_o (clk_lane_ready),
.hs_valid_i(1'b1),
.lp_request_i (1'b0),
.lp_data_i(8'h00),
.lp_valid_i(1'b0),
.lp_ready_o(),
.serdes_data_o(serdes_data[g_lanes*8+:8]),
.lp_txp_o(dsi_clk_lp_p_o),
.lp_txn_o(dsi_clk_lp_n_o),
.lp_oe_o(dsi_clk_lp_oe)
);
assign serdes_oe_lane [g_lanes] = ~dsi_clk_lp_oe;
assign dsi_clk_lp_oe_o = dsi_clk_lp_oe;
generate
genvar gi, gj;
for(gi=0;gi<8;gi=gi+1)
begin
assign serdes_data_interleaved[(g_lanes+1)*gi+g_lanes] = serdes_data[8*g_lanes+gi];
for(gj=0;gj<g_lanes; gj=gj+1)
begin
assign serdes_data_interleaved[(g_lanes+1)*gi+gj] = serdes_data[8*gj+gi];
end
end
endgenerate
dphy_serdes
#(
.sys_w (g_lanes + 1) ,
.dev_w (g_lanes * 8 + 8)
) U_Serdes (
.DATA_OUT_FROM_DEVICE(serdes_data_interleaved),
.DATA_OUT_TO_PINS_P({dsi_clk_p_o, dsi_hs_p_o }),
.DATA_OUT_TO_PINS_N({dsi_clk_n_o, dsi_hs_n_o }),
.CLK_IN(clk_phy_i),
.CLK_DIV_IN(clk_sys_i),
.LOCKED_IN(pll_locked_i),
.CLK_RESET(~rst_n_i),
.IO_RESET(~rst_n_i),
.OE(serdes_oe_lane));
////////////////
// Packet layer
///////////////
wire p_req, p_islong, p_dreq, p_last;
wire [5:0] p_type;
wire [15:0] p_command, p_wcount;
wire [g_pixel_width-1:0] p_payload;
dsi_packet_assembler
#(
.g_num_lanes(g_lanes),
.g_bytes_per_clock(g_pixels_per_clock)
) U_PktAsm (
.clk_i(clk_sys_i),
.rst_n_i(rst_n_i),
.p_req_i(p_req),
.p_islong_i(p_islong),
.p_type_i(p_type),
.p_wcount_i(p_wcount),
.p_command_i(p_command),
.p_dreq_o(p_dreq),
.p_dlast_o(p_dlast),
.p_payload_i(p_payload),
.p_last_i(p_last),
.phy_d_o(phy_hs_data),
.phy_hs_request_o(phy_hs_request),
.phy_hs_dreq_i(phy_hs_ready_lane[0]),
.phy_dvalid_o(phy_hs_valid)
);
////////////////
// Test Screen generator
///////////////
wire fifo_frame_tgen, fifo_wr_tgen;
wire [g_pixel_width-1:0] fifo_pixel_tgen;
wire fifo_empty, fifo_rd;
wire [g_pixel_width:0] fifo_dout;
wire fifo_frame_int, fifo_wr_int, fifo_full_int;
wire [g_pixel_width-1:0] fifo_pixels_int;
wire tgen_enable;
dsi_test_image_gen
U_TestImgGen
(
.clk_i(clk_sys_i),
.rst_n_i(rst_n_i),
.fifo_full_i(fifo_full_int),
.fifo_frame_o(fifo_frame_tgen),
.fifo_pixel_o(fifo_pixel_tgen),
.fifo_we_o(fifo_wr_tgen),
.host_a_i(host_a_i),
.host_d_i(host_d_i),
.host_wr_i(host_wr_i),
.test_enable_o(tgen_enable)
);
assign fifo_frame_int = (tgen_enable ? fifo_frame_tgen : fifo_frame_i);
assign fifo_pixels_int = (tgen_enable ? fifo_pixel_tgen : fifo_pixels_i);
assign fifo_wr_int = (tgen_enable ? fifo_wr_tgen : fifo_wr_i);
assign fifo_full_o = fifo_full_int;
reg fifo_rd_d0, fifo_got_frame;
///////////////
// Image timing
///////////////
dsi_timing_gen U_TimingGen
(
.clk_i(clk_sys_i),
.rst_n_i(rst_n_i),
.fifo_empty_i(fifo_empty),
.fifo_rd_o(fifo_rd),
.fifo_pixels_i(fifo_dout[g_pixel_width-1:0]),
.fifo_frame_i(fifo_dout[g_pixel_width]),
.fifo_got_frame_i(fifo_got_frame),
.p_req_o(p_req),
.p_islong_o(p_islong),
.p_type_o(p_type),
.p_wcount_o(p_wcount),
.p_command_o(p_command),
.p_payload_o(p_payload),
.p_dreq_i(p_dreq),
.p_last_o(p_last),
.host_a_i(host_a_i),
.host_d_i(host_d_i),
// .host_d_o(host_d_o),
.host_wr_i(host_wr_i)
);
////////////////
/// Pixel Buffer
////////////////
dsi_fifo #(
.g_width(g_pixel_width + 1),
.g_size(g_fifo_size)
) U_PixFifo (
.clk_i(clk_sys_i),
.rst_n_i(rst_n_i),
.d_i({fifo_frame_int, fifo_pixels_int}),
.we_i(fifo_wr_int),
.full_o(fifo_full_int),
.q_o(fifo_dout),
.rd_i(fifo_rd),
.empty_o(fifo_empty)
);
always@(posedge clk_sys_i)
if(!rst_n_i)
begin
fifo_rd_d0 <= 0;
fifo_got_frame <= 0;
end else begin
fifo_rd_d0 <= fifo_rd;
if(fifo_wr_int && fifo_frame_int)
fifo_got_frame <= 1;
else if (fifo_rd && fifo_dout[g_pixel_width])
fifo_got_frame <= 0;
end // else: !if(!rst_n_i)
////////////////
// Host regs
////////////////
reg [11:0] r_tick_div, tick_count;
always@(posedge clk_sys_i)
begin
if (!rst_n_i)
tick_count <= 0;
else begin
if(tick_count == r_tick_div)
begin
tick <= 1;
tick_count <= 0;
end else begin
tick <= 0;
tick_count <= tick_count + 1;
end
end // else: !if(!rst_n_i)
end // always@ (posedge clk_sys_i)
reg [31:0] host_d_self;
always@(posedge clk_sys_i)
if(!rst_n_i)
begin
r_tick_div <= 0;
r_dsi_clk_en <= 0;
lp_request <= 0;
host_d_self <= 0;
dsi_reset_n_o <= 0;
end else if(host_wr_i) begin
case(host_a_i)
`REG_TICK_DIV: r_tick_div <= host_d_i;
`REG_DSI_CTL: begin
r_dsi_clk_en <= host_d_i[0];
lp_request <= host_d_i[1];
end
`REG_LP_TX:
if(lp_ready) begin
lp_valid <= host_d_i[8];
lp_data <= host_d_i[7:0];
end
`REG_GPIO: begin
dsi_reset_n_o <= host_d_i[0];
end
endcase // case (host_a_i)
end else begin
lp_valid <= 0;
case(host_a_i)
`REG_DSI_CTL: begin
host_d_self[0] <= r_dsi_clk_en;
host_d_self[31:2] <= 0;
host_d_self[1] <= lp_ready;
end
default:
host_d_self <= 0;
endcase // case (host_a_i)
end // else: !if(host_wr_i)
assign host_d_o = host_d_self;
endmodule // dsi_core