//---------------------------------------------------------------------
// File name  : tb_top.v
// Module name: tb_top
// Created by : 
// ---------------------------------------------------------------------
// Release history
// ----------------------------------------------------------------------------------
// Ver:    |  Author    | Mod. Date    | Changes Made:
// ----------------------------------------------------------------------------------
// V1.0    | Caojie     | 07/17/19     | Initial version 
// ----------------------------------------------------------------------------------
// V1.1    | Caojie     | 10/12/19     | Scaler IP update 
// ----------------------------------------------------------------------------------

`timescale 1ns / 1ps

`include "vfb_defines.v"

`define	YC444  //为测试不同数据格式，根据Scaler IP定义来修改, YC444, YC422, SINGLE
`define	PARAM_DYNAMIC_CTRL //根据Scaler IP定义来确定是否 define

module tb();

//===================================================================
//Memory mode up or down choice
parameter UP_DOWN_SEL			 = 1'b0;  //0:放大；1:缩小, 根据缩放分辨率需做选择

parameter SCALER_CLK_FREQ		   = 120.000;//clock frequency, unit: MHz
parameter SCALER_CLK_PERIOD		   = 1000.0/SCALER_CLK_FREQ;//unit: ns

//========================================================
//parameters
parameter BMP_VIDEO_FORMAT		   = "WxH_xHz"; //video format
parameter BMP_PIXEL_CLK_FREQ	   = 75.000;//pixel clock frequency, unit: MHz
parameter BMP_PIXEL_CLK_PERIOD	   = 1000.0/BMP_PIXEL_CLK_FREQ; //unit: ns
parameter BMP_WIDTH				   = 160;
parameter BMP_HEIGHT			   = 120;
parameter BMP_OPENED_NAME		   = "../../tb/pic/img160.bmp";
parameter BMP_REPEAT			   = 1'b1;  //0:bmp increase  , 1:bmp repeat 
parameter BMP_LINK				   = 1'b0;  //0:单像素；1:双像素
								   
parameter BMP_OUT_VIDEO_FORMAT	   = "WxH_xHz"; //video format
parameter BMP_OUT_PIXEL_CLK_FREQ   = UP_DOWN_SEL ? SCALER_CLK_FREQ : 100.000;//pixel clock frequency, unit: MHz
parameter BMP_OUT_PIXEL_CLK_PERIOD = 1000.0/BMP_OUT_PIXEL_CLK_FREQ; //unit: ns
parameter BMP_OUTPUTED_WIDTH	   = 200;
parameter BMP_OUTPUTED_HEIGHT	   = 150;
parameter BMP_OUTPUTED_NAME		   = "../../tb/pic/out0_001.bmp";
parameter BMP_OUTPUTED_NUMBER	   = 16'd3;

//=========================================================
parameter CONFIG_VIDEO_FORMAT	 = UP_DOWN_SEL ? BMP_VIDEO_FORMAT   : BMP_OUT_VIDEO_FORMAT  ;
parameter CONFIG_PIXEL_CLK_FREQ  = UP_DOWN_SEL ? BMP_PIXEL_CLK_FREQ : BMP_OUT_PIXEL_CLK_FREQ;
parameter CONFIG_HOR_RESOLUTION  = UP_DOWN_SEL ? BMP_WIDTH		    : BMP_OUTPUTED_WIDTH	;
parameter CONFIG_VER_RESOLUTION  = UP_DOWN_SEL ? BMP_HEIGHT		    : BMP_OUTPUTED_HEIGHT   ;

//-------------------------------------------------
parameter DDR_REF_CLK_FREQ   = 50.000;//ddr reference clock frequency, unit: MHz
parameter DDR_REF_CLK_PERIOD = 1000.0/DDR_REF_CLK_FREQ; //unit: ns

//=======================================================
reg  ddr_ref_clk;      // 50M
reg  pixel_clock;
reg  scaler_clk;

reg  rst_n;

reg	 output_pixel_clk;

reg	 rd_gen ;

reg  param_update_i;

//-------------------------------------------------
//ddr
wire        dma_clk  ;  

//-------------------------------------------------
//memory interface
wire [13:0] ddr_addr   ;
wire [2:0]  ddr_bank   ;
wire        ddr_cs     ;
wire        ddr_ras    ;
wire        ddr_cas    ;
wire        ddr_we     ;
wire        ddr_ck     ;
wire        ddr_ck_n   ;
wire        ddr_cke    ;
wire        ddr_odt    ;
wire        ddr_reset_n;
wire [1:0]  ddr_dm     ;
wire [15:0] ddr_dq     ;
wire [1:0]  ddr_dqs    ;
wire [1:0]  ddr_dqs_n  ;

wire        cmd_ready          ;
wire[2:0]   cmd                ;
wire        cmd_en             ;
wire[5:0]   app_burst_number   ;
wire[27:0]  addr               ;
wire        wr_data_rdy        ;
wire        wr_data_en         ;//
wire        wr_data_end        ;//
wire[127:0] wr_data            ;   
wire[15:0]  wr_data_mask       ;   
wire        rd_data_valid      ;  
wire        rd_data_end        ;//unused 
wire[127:0] rd_data            ;   
wire        init_calib_complete;

//------------
//dirver
wire	   vsync; 
wire	   hsync; 
wire	   data_valid; 
wire [7:0] data0_r; 
wire [7:0] data0_g;
wire [7:0] data0_b;
wire [7:0] data1_r ; 
wire [7:0] data1_g ;
wire [7:0] data1_b ;

//-------------------------
//frame buffer in
wire                        vfb_clk_in ;
wire                        vfb_vs_in  ;
wire                        vfb_de_in  ;
wire [`DEF_VIDEO_WIDTH-1:0] vfb_data_in;

//--------
//Scaler
reg [11:0] vin_hor_size;
reg [11:0] vin_ver_size;
reg [11:0] vout_hor_size;
reg [11:0] vout_ver_size;

reg [35:0] vin_hor_size_ext;
reg [35:0] vin_ver_size_ext;

reg [23:0] h_shrink_factor; //缩放因子，无符号定点数，8bit整数，16bit小数，vin_hor/vout_hor
reg [23:0] v_shrink_factor; //缩放因子，无符号定点数，8bit整数，16bit小数，vin_ver/vout_ver

wire		scl_vin_vs;  
wire		scl_vin_de;
wire [23:0] scl_vin_data;
                       
wire		sclout_vs  ;  
wire		sclout_hs  ;  
wire		sclout_de  ;
wire [23:0] sclout_data;

//----------------  
//config
wire  [15:0] data_valid_xst_o;
wire  [15:0] data_valid_xed_o;
wire  [15:0] data_valid_yst_o;
wire  [15:0] data_valid_yed_o;
wire  [15:0] hor_sync_time_o ;
wire  [15:0] ver_sync_time_o ;
wire  [15:0] hor_total_time_o;
wire  [15:0] ver_total_time_o;

//-------------------
//syn_code
wire		syn_off0_re;  // ofifo read enable signal
wire		syn_off0_vs;
wire		syn_off0_hs;

wire                        off0_syn_de  ;
wire [`DEF_VIDEO_WIDTH-1:0] off0_syn_data;

//-----------------
//monitor rgb input
wire		m_clk;
wire		m_vs_rgb;  
wire		m_hs_rgb;  
wire		m_de_rgb;  
wire [7:0]  m_data0_r;
wire [7:0]  m_data0_g;
wire [7:0]  m_data0_b;
wire [7:0]  m_data1_r;
wire [7:0]  m_data1_g;
wire [7:0]  m_data1_b;

//=====================================================
GSR GSR(.GSRI(1'b1));

//==============================================  
initial begin
  $fsdbDumpfile("tb.fsdb");
  $fsdbDumpvars;
end

//=====================================================
//pixel_clk
initial
  begin
    ddr_ref_clk      = 1'b0;
	pixel_clock	     = 1'b0;
	scaler_clk	     = 1'b0;
	output_pixel_clk = 1'b0;
  end

always  #(DDR_REF_CLK_PERIOD/2.0) ddr_ref_clk = ~ddr_ref_clk;

always  #(BMP_PIXEL_CLK_PERIOD/2.0) pixel_clock = ~pixel_clock;

always  #(SCALER_CLK_PERIOD/2.0) scaler_clk = ~scaler_clk;

always  #(BMP_OUT_PIXEL_CLK_PERIOD/2.0) output_pixel_clk = ~output_pixel_clk;

//=====================================================
//rst_n
initial
  begin
	rst_n=1'b0;
	
	#2000;
	rst_n=1'b1;
end

initial
  begin
	param_update_i=1'b0;
	
	#2050;
	param_update_i=1'b1;
end


//==================================================
//缩放因子计算
initial
  begin
	vin_hor_size   = BMP_WIDTH; 
	vin_ver_size   = BMP_HEIGHT; 
	vout_hor_size  = BMP_OUTPUTED_WIDTH;	
	vout_ver_size  = BMP_OUTPUTED_HEIGHT;
	
	vin_hor_size_ext  = vin_hor_size<<16;	
	vin_ver_size_ext  = vin_ver_size<<16;
	h_shrink_factor   = vin_hor_size_ext/vout_hor_size;
	v_shrink_factor   = vin_ver_size_ext/vout_ver_size; 
  end 

//==================================================
//video driver
driver #
(
	.BMP_VIDEO_FORMAT	(BMP_VIDEO_FORMAT   ),
	.BMP_PIXEL_CLK_FREQ (BMP_PIXEL_CLK_FREQ ),
	.BMP_WIDTH		    (BMP_WIDTH	        ),
	.BMP_HEIGHT		    (BMP_HEIGHT	        ),
	.BMP_OPENED_NAME	(BMP_OPENED_NAME    )
)
driver_inst
(
	.link_i	       (BMP_LINK   ), //0,单像素；1，双像素
	.repeat_en     (BMP_REPEAT ),
	.video_gen_en  (init_calib_complete	   ),
	.pixel_clock   (pixel_clock),
	.vsync	       (vsync	   ),//负极性 
	.hsync	       (hsync	   ),//负极性 
	.data_valid    (data_valid ),
	.data0_r       (data0_r	   ), 
	.data0_g       (data0_g	   ),
	.data0_b       (data0_b	   ), 
	.data1_r       (data1_r	   ), 
	.data1_g       (data1_g	   ),
	.data1_b       (data1_b	   )
);

//======================================================
`ifdef VIDEO_WIDTH_16    
	assign vfb_clk_in  = pixel_clock;
	assign vfb_vs_in   = vsync;
	assign vfb_de_in   = data_valid;
	assign vfb_data_in = {data0_b[7:3],data0_g[7:2],data0_r[7:3]};//{b,g,r}  RGB565
`endif

`ifdef VIDEO_WIDTH_24    
	assign vfb_clk_in  = pixel_clock;
	assign vfb_vs_in   = vsync;
	assign vfb_de_in   = data_valid;
	assign vfb_data_in = {data0_b,data0_g,data0_r};//{b,g,r}
`endif  

//==================================================================
//SRAM 控制模块 
vfb_top vfb_top_inst
( 
	.I_rst_n		    (init_calib_complete ),//rst_n            ),
	.I_dma_clk		    (dma_clk          ),   //sram_clk         ),
	.I_wr_halt		    (1'd0             ), //1:halt,  0:no halt
	.I_rd_halt		    (1'd0             ), //1:halt,  0:no halt
	// video data input           
	.I_vin0_clk		    (vfb_clk_in   ),
	.I_vin0_vs_n	    (vfb_vs_in    ),
	.I_vin0_de		    (vfb_de_in    ),
	.I_vin0_data	    (vfb_data_in  ),
	// video data output          
	.I_vout0_clk	    (output_pixel_clk ),
	.I_vout0_vs_n	    (~scl_vin_vs      ),//负极性
	.I_vout0_de		    (scl_vin_de       ),
	.O_vout0_den	    (off0_syn_de      ),
	.O_vout0_data	    (off0_syn_data    ),
	.O_vout0fifo_empty  (vout0fifo_empty  ),
	// ddr write request
	.cmd_ready          (cmd_ready          ),
	.cmd                (cmd                ),//0:write;  1:read
	.cmd_en             (cmd_en             ),
	.app_burst_number   (app_burst_number   ),
	.addr               (addr               ),//[ADDR_WIDTH-1:0]
	.wr_data_rdy        (wr_data_rdy        ),
	.wr_data_en         (wr_data_en         ),//
	.wr_data_end        (wr_data_end        ),//
	.wr_data            (wr_data            ),//[DATA_WIDTH-1:0]
	.wr_data_mask       (wr_data_mask       ),
	.rd_data_valid      (rd_data_valid      ),
	.rd_data_end        (rd_data_end        ),//unused 
	.rd_data            (rd_data            ),//[DATA_WIDTH-1:0]
	.init_calib_complete(init_calib_complete)
);   

//----------------------------------------------------- 
//ddr3 ip
DDR3_Memory_Interface_Top DDR3_Memory_Interface_Top_inst 
(
	.clk                (ddr_ref_clk        ),
	.rst_n              (rst_n              ),   //rst_n
	.app_burst_number   (app_burst_number   ),
	.cmd_ready          (cmd_ready          ),
	.cmd                (cmd                ),
	.cmd_en             (cmd_en             ),
	.addr               (addr               ),
	.wr_data_rdy        (wr_data_rdy        ),
	.wr_data            (wr_data            ),
	.wr_data_en         (wr_data_en         ),
	.wr_data_end        (wr_data_end        ),
	.wr_data_mask       (wr_data_mask       ),
	.rd_data            (rd_data            ),
	.rd_data_valid      (rd_data_valid      ),
	.rd_data_end        (rd_data_end        ),
	.sr_req             (                   ),
	.ref_req            (                   ),
	.sr_ack             (                   ),
	.ref_ack            (                   ),
	.init_calib_complete(init_calib_complete),
	.clk_out            (dma_clk            ),
	`ifdef ECC
	.ecc_err            (                   ),
	`endif
	.burst              (1'b1               ),
	// mem interface
	.ddr_rst            (                   ),
	.O_ddr_addr         (ddr_addr           ),
	.O_ddr_ba           (ddr_bank           ),
	.O_ddr_cs_n         (ddr_cs             ),
	.O_ddr_ras_n        (ddr_ras            ),
	.O_ddr_cas_n        (ddr_cas            ),
	.O_ddr_we_n         (ddr_we             ),
	.O_ddr_clk          (ddr_ck             ),
	.O_ddr_clk_n        (ddr_ck_n           ),
	.O_ddr_cke          (ddr_cke            ),
	.O_ddr_odt          (ddr_odt            ),
	.O_ddr_reset_n      (ddr_reset_n        ),
	.O_ddr_dqm          (ddr_dm             ),
	.IO_ddr_dq          (ddr_dq             ),
	.IO_ddr_dqs         (ddr_dqs            ),
	.IO_ddr_dqs_n       (ddr_dqs_n          )
);
  
//-------------------------------------------------------------
//ddr3 model 
ddr3_model ddr3_model_inst
(
	.rst_n    (ddr_reset_n),
	.ck       (ddr_ck     ),
	.ck_n     (ddr_ck_n   ),
	.cke      (ddr_cke    ),
	.cs_n     (ddr_cs     ),
	.ras_n    (ddr_ras    ),
	.cas_n    (ddr_cas    ),
	.we_n     (ddr_we     ),
	.dm_tdqs  (ddr_dm     ),
	.ba       (ddr_bank   ),
	.addr     (ddr_addr   ),
	.dq       (ddr_dq     ),
	.dqs      (ddr_dqs    ),
	.dqs_n    (ddr_dqs_n  ),
	.tdqs_n   (           ),
	.odt      (ddr_odt    )
);

//==========================================================================
//scaler 
`ifdef VIDEO_WIDTH_16    
	assign scl_vin_data = {off0_syn_data[15:11],3'd0,off0_syn_data[10:5],2'd0,off0_syn_data[4:0],3'd0};//{b,g,r}  RGB565
`endif

`ifdef VIDEO_WIDTH_24    
	assign scl_vin_data = {{off0_syn_data[23:16],off0_syn_data[15:8],off0_syn_data[7:0]};//{b,g,r}
`endif  


Scaler_Top Scaler_Top_inst
(
	.I_reset		(~rst_n			      ),//高有效
	.I_sysclk		(scaler_clk		      ),
`ifdef PARAM_DYNAMIC_CTRL
	.I_param_update	(param_update_i	      ),
	.I_vin_hsize	(vin_hor_size	      ),
	.I_vin_vsize	(vin_ver_size	      ),
	.I_vout_hsize	(vout_hor_size	      ),
	.I_vout_vsize	(vout_ver_size	      ),
	.I_hor_skfactor	(h_shrink_factor      ),
	.I_ver_skfactor	(v_shrink_factor      ),	
`endif  
	.I_vin_clk		(output_pixel_clk  	  ),	
	.I_vin_ref_vs	(~syn_off0_vs	      ),//正极性  
	.I_vin_ref_de	(syn_off0_re	      ),
	.O_vin_vs_req	(scl_vin_vs		      ),//正极性					  
	.O_vin_de_req	(scl_vin_de		      ),
	.I_buff_ready   (~vout0fifo_empty     ),
	.I_up_down_sel	(UP_DOWN_SEL	      ),								
	.I_vin_de_cpl	(off0_syn_de	      ),
`ifdef YC444					          
	.I_vin_data0_cpl(scl_vin_data[ 7: 0]  ), 
	.I_vin_data1_cpl(scl_vin_data[15: 8]  ), 
	.I_vin_data2_cpl(scl_vin_data[23:16]  ), 
	.O_vout0_data   (sclout_data[ 7: 0]   ),			  
	.O_vout1_data   (sclout_data[15: 8]   ),
	.O_vout2_data   (sclout_data[23:16]   ),
`endif                                      
`ifdef YC422	                            
	.I_vin_data0_cpl(scl_vin_data[ 7: 0]  ), 
	.I_vin_data1_cpl(scl_vin_data[15: 8]  ), 
	.O_vout0_data   (sclout_data[ 7: 0]   ),
	.O_vout1_data   (sclout_data[15: 8]   ),
`endif                                      
`ifdef SINGLE	      		                
	.I_vin_data0_cpl(scl_vin_data[ 7: 0]  ), 
	.O_vout0_data   (sclout_data[ 7: 0]   ),
`endif                                   
	.I_vout_clk		(output_pixel_clk     ),									
	.O_vout_vs		(sclout_vs	          ),//正极性					   
	.O_vout_de		(sclout_de	          ) 
);								   

assign sclout_hs = ~syn_off0_hs;

//=====================================================================
//延迟一段时间后开始读数据
  initial 
	begin
	  rd_gen = 1'b0 ;
	  forever
		begin
		  #100; //ns
		  if ( init_calib_complete == 1'b1 )
			begin
			  #1000000;//ns  //根据图像大小修改延迟时间
			  rd_gen=1'b1;
			end 
		  else 
			begin
			 rd_gen=1'b0;
			end 
		end 
	end 

//-----------------------------------------------------
//config
config_m#
(
	.OUTPUT_VIDEO_FORMAT   (CONFIG_VIDEO_FORMAT  ),
	.OUTPUT_PIXEL_CLK_FREQ (CONFIG_PIXEL_CLK_FREQ),
	.OUTPUT_HOR_RESOLUTION (CONFIG_HOR_RESOLUTION),
	.OUTPUT_VER_RESOLUTION (CONFIG_VER_RESOLUTION)
)
config_m_inst
(
	.data_valid_xst_o (data_valid_xst_o), 
	.data_valid_xed_o (data_valid_xed_o), 
	.data_valid_yst_o (data_valid_yst_o), 
	.data_valid_yed_o (data_valid_yed_o),
	.hor_sync_time_o  (hor_sync_time_o ),
	.ver_sync_time_o  (ver_sync_time_o ),
	.hor_total_time_o (hor_total_time_o), 
	.ver_total_time_o (ver_total_time_o)  
);

//---------------------------------------------------
//输出同步时序模块
syn_code_16 syn_code_16_inst
(
	.cpu2out_xst_reg   (data_valid_xst_o),
	.cpu2out_xed_reg   (data_valid_xed_o),
	.cpu2out_yst_reg   (data_valid_yst_o),
	.cpu2out_yed_reg   (data_valid_yed_o),
	.cpu2out_hsync_reg (hor_sync_time_o ),
	.cpu2out_vsync_reg (ver_sync_time_o ),
	.cpu2out_hed_reg   (hor_total_time_o),
	.cpu2out_ved_reg   (ver_total_time_o),
	.hor_res0		   (data_valid_xed_o-data_valid_xst_o+1'b1),
	.ver_res0		   (data_valid_yed_o-data_valid_yst_o+1'b1),
	.vs_sel			   (1'b0			),//sim only
	.rst_n			   (rd_gen		    ),
	.pxl_clk		   (output_pixel_clk),
	.e_vs			   (1'b0			),//sim only
	.off0_syn_data	   (				),
	.syn_off0_re	   (syn_off0_re	    ),
	.syn_off0_vs	   (syn_off0_vs	    ),//负极性
	.syn_off0_hs	   (syn_off0_hs	    ),//负极性
	.syn_clip0_vs	   (				),
	.syn_clip0_hs	   (				),
	.syn_clip0_de	   (				),
	.syn_clip0_data	   (				)
);

//==========================================================
//monitor
`ifdef YC444
	assign m_clk	 = output_pixel_clk ;
	assign m_vs_rgb  = ~sclout_vs           ;
	assign m_hs_rgb  = ~sclout_hs           ;
	assign m_de_rgb  = sclout_de           ;
	assign m_data0_r = sclout_data[ 7: 0]  ;
	assign m_data0_g = sclout_data[15: 8]  ;
	assign m_data0_b = sclout_data[23:16]  ;
	assign m_data1_r = 8'd0	            ;
	assign m_data1_g = 8'd0	            ;
	assign m_data1_b = 8'd0	            ;
`endif 

`ifdef YC422	
	assign m_clk	 = output_pixel_clk ;
	assign m_vs_rgb  = ~sclout_vs           ;
	assign m_hs_rgb  = ~sclout_hs           ;
	assign m_de_rgb  = sclout_de           ;
	assign m_data0_r = sclout_data[ 7: 0]  ;
	assign m_data0_g = sclout_data[ 7: 0]  ;
	assign m_data0_b = sclout_data[ 7: 0]  ;
	assign m_data1_r = 8'd0	            ;
	assign m_data1_g = 8'd0	            ;
	assign m_data1_b = 8'd0	            ;
`endif 

`ifdef SINGLE	
	assign m_clk	 = output_pixel_clk ;
	assign m_vs_rgb  = ~sclout_vs           ;
	assign m_hs_rgb  = ~sclout_hs           ;
	assign m_de_rgb  = sclout_de           ;
	assign m_data0_r = sclout_data[ 7: 0]  ;
	assign m_data0_g = sclout_data[ 7: 0]  ;
	assign m_data0_b = sclout_data[ 7: 0]  ;
	assign m_data1_r = 8'd0	            ;
	assign m_data1_g = 8'd0	            ;
	assign m_data1_b = 8'd0	            ;
`endif 
	
initial 
  begin
	force m_vs_rgb = 1'b0;
	
	@(posedge monitor_inst.video2bmp_en)
	  #100000;
	  release m_vs_rgb;
  end 

monitor#
(
  .BMP_OUTPUTED_WIDTH  (BMP_OUTPUTED_WIDTH ),
  .BMP_OUTPUTED_HEIGHT (BMP_OUTPUTED_HEIGHT),
  .BMP_OUTPUTED_NAME   (BMP_OUTPUTED_NAME  ),
  .BMP_OUTPUTED_NUMBER (BMP_OUTPUTED_NUMBER)
)
monitor_inst
(
  .link_i	    (BMP_LINK	), //0,单像素；1，双像素
  .video2bmp_en (rst_n		),
  .pixel_clock  (m_clk		), 
  .vsync		(m_vs_rgb	), //负极性	   
  .hsync		(m_hs_rgb	), //负极性	   
  .data_valid   (m_de_rgb	), 
  .data0_r	    (m_data0_r	),	
  .data0_g	    (m_data0_g	),
  .data0_b	    (m_data0_b	),
  .data1_r	    (m_data1_r	),	
  .data1_g	    (m_data1_g	),
  .data1_b	    (m_data1_b	)
);
		  
endmodule
