456 lines
12 KiB
Coq
456 lines
12 KiB
Coq
|
/*
|
||
|
* 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
|
||
|
|
||
|
|
||
|
|