【高云GW5AT-LV60 开发套件试用体验】四、软件项目开发-LCD屏幕显示
GW5AT-LV60 FPGA的图像开发板, 使用的是高云半导体发布的Arora-V:GW5AT-LV60芯片,采用22nm先进工艺。
GW5AT-LV60 FPGA的图像开发板,采用核心板与底板的分离设计, 其中核心板为38*38的尺寸,将所有硬件资源扇出到B2B接口的同时,也集成了DDR3、FLASH等板载资源,方便客户二次开发,以及最终产品集成。
GW5AT-LV60 FPGA的图像开发板,搭配了一块奥唯思 VD-MIPI-5.5TH屏幕。通过编程,我们可以实现GW5AT-LV60 FPGA芯片对LCD的显示控制。
一、LCD 显示屏与FPGA的硬件连接
在GW5AT-LV60 FPGA的图像开发板上,VD-MIPI-5.5TH屏幕通过 底板上的连接器J10,与底板连接,在通过底板与核心板之间的B2B连接器J5,J6,连接到核心板上的J1,J2,实现LCD接口信号与FPGA芯片信号的互连。
1、LCD屏幕接口的接口信号

2、LCD MIP接口信号与FPGA Pin的连接关系

3、LCD的 LED背光控制
LED_PWM连接U16,通过背光驱动芯片实现LEDA\LEDK的脉冲电流输出。


4、核心板上的LED_RED作为状态输出灯,
LED_RED作为状态输出灯,连接在FPGA的H13 Pin上。

5、时钟输入
有源晶振的输出CLK25M,接FPGA的L6 Pin 作为时钟输入。


二、LCD 显示软件工程搭建
使用 Gowin EDA 软件,针对 GW5AT-LV60UG225C2/I1 芯片和 VF-G60K225 开发板,实现VD-MIPI-5.5TH LCD 屏幕显示测试的软件工程搭建的详细操作步骤如下,
步骤1:启动Gowin EDA软件
- 双击桌面图标"Gowin IDE" , 启动Gowin EDA 软件;
- 首次启动会提示选择工作目录,建议设置为:D:\Gowin_Projects\MIPI_LCD_Test
本次实验我们使用的Gowin EDA 软件是 V1.9.11版本。

步骤 2:创建新工程
- 点击菜单栏
"File" → "New" → "Project"
- 在弹出的对话框中:
o输入工程名称:mipi_color_bar
o工程路径保持默认(或选择自定义路径)
o 点击 "OK"

步骤 3:配置工程目标器件
- 在弹出的 "Device Configuration" 窗口中:
- 系列 (Series):选择 "GW5AT"
- 型号 (Device):选择 "GW5AT-LV60UG225C2/I1"
- 封装 (Package):选择 "UBGA225"
- 速度等级 (Speed Grade):选择 "C2/l1"
- 点击 "OK" 完成配置



步骤 4:创建顶层模块文件
-
右键点击工程窗口中的 "Design Files" → "New" → "Verilog
File"

-
文件名输入:top_module.v,点击 "OK"

-
在编辑器中输入以下代码:
module top_module(
input
clk_25m, // 25MHz系统时钟
//input
rst_n, // 系统复位,低有效
output
led,
////////////////////////////////////////////////////////////////
// DSI PWM & Reset Control
output
dsi_reset_o, // MIPI-DSI
LCD Reset // LCD复位,低有效
output
dsi_pwm_o, // MIPI-DSI
LCD Reset // LCD背光使能
// MIPI-DSI TXC / TXD
inout
TX_CLK_P, // MIPI时钟正
inout
TX_CLK_N, // MIPI时钟负
inout
[3:0] TX_DATA_P, // MIPI数据正
inout
[3:0] TX_DATA_N // MIPI数据负
);
parameter
SIM_DATA = 0;
//
内部信号定义
////////////////////////////////////////////////////////////////
// System Clock Ticks
wire
w_ustick;
wire
w_mstick;
//
Instantiate the Unit Under Test (UUT)
ClockTick1Ms
clk_tick_gen (
.clk_i (clk_25m),
.ustickcnt_i (25),
.ustickinc_i (1),
.mstickcnt_i (1000),
.ustick_o (w_ustick),
.mstick_o (w_mstick)
);
////////////////////////////////////////////////////////////////
// DSI PLL & Clock
wire
w_dsi_pll_0,
w_dsi_pll_90, w_dsi_pll_sclk;
wire
w_dsi_pll_lock;
dsi_pll
dsi_pll_inst (
.lock (w_dsi_pll_lock), //output lock
.clkout0 (w_dsi_pll_0), //output clkout0
.clkout1 (w_dsi_pll_90), //output clkout1
.clkout2 (w_dsi_pll_sclk), //output clkout2
.clkin (clk_25m) //input clkin
);
wire
w_dsi_clk =
w_dsi_pll_sclk;
wire
w_dsi_rst, w_dsi_rstn;
Reset
dsi_rst (.clk_i(w_dsi_clk), .rstn_i(1), .locked_i(w_dsi_pll_lock),
.rstn_o(w_dsi_rstn), .rst_o(w_dsi_rst));
////////////////////////////////////////////////////////////////
// DSI Config
wire
w_dsi_init_busy;
wire
w_dsi_init_done;
// Control
wire
w_dsi_init_cmd_lvalid_o;
wire
w_dsi_init_cmd_we_o;
wire
[7:0] w_dsi_init_cmd_data_o;
wire
w_dsi_init_cmd_ready_i;
dsi_init_et055rfpb93_lp
.clk_i (w_dsi_clk),
.rst_i (w_dsi_rst),
.ustick_i (w_ustick), // Microsecond
Tick. Used for register delay control.
.mstick_i (w_mstick), // Millisecond Tick.
Used for register delay control.
.resetn_o (dsi_reset_o), // DSI
Resetn Output (delay for >= 10us)
.cmd_lvalid_o(w_dsi_init_cmd_lvalid_o),
.cmd_we_o (w_dsi_init_cmd_we_o),
.cmd_data_o (w_dsi_init_cmd_data_o),
.cmd_ready_i
(w_dsi_init_cmd_ready_i),
.busy_i (w_dsi_init_busy), // Attach
to HS_EN. Must assert then deassert.
.done_o (w_dsi_init_done) // HS Command Done.
);
// assign
led = w_dsi_init_done;
wire
w_dsi_tx_lp_p_o,
w_dsi_tx_lp_n_o;
//
时钟生成模块
DSILPInitCtl
dsi_init_lp_io (
.clk_i (w_dsi_clk),
.rst_i (w_dsi_rst),
.lptick_i (26), // LP Tick. Generate 5MHz LP configuration
clock.
.cmd_lvalid_i(w_dsi_init_cmd_lvalid_o), // 1
will launch LP command sequence.
.cmd_data_i (w_dsi_init_cmd_data_o),
.cmd_we_i (w_dsi_init_cmd_we_o), // 1
will issue new byte write. Must deassert after ready_o.
.cmd_ready_o (w_dsi_init_cmd_ready_i),
.busy_o
(w_dsi_init_busy),
.lp_p_o (w_dsi_tx_lp_p_o),
.lp_n_o (w_dsi_tx_lp_n_o)
);
//
LCD控制模块
////////////////////////////////////////////////////////////////
// LCD Driver
localparam
CLOCK_MAIN = 131250000; // 131.25MHz Pixel Clock
wire
w_lcd_de;
wire
w_lcd_hs;
wire
w_lcd_vs;
wire
w_lcd_request;
wire
[7:0] w_lcd_red, w_lcd_red2;
wire
[7:0] w_lcd_green, w_lcd_green2;
wire
[7:0] w_lcd_blue, w_lcd_blue2;
wire
[11:0] w_lcd_xpos, w_lcd_ypos;
wire
[23:0] w_lcd_data;
lcd_display
.clk (w_dsi_clk),
.rst_n (w_dsi_rstn),
.lcd_xpos (w_lcd_xpos),//lcd horizontal coordinate
.lcd_ypos (w_lcd_ypos),//lcd vertical coordinate
.lcd_data (w_lcd_data) //lcd data
);
reg
[15:0] r_lcd_driver_resetn = 0;
always
@(posedge w_dsi_clk or negedge w_dsi_init_done) begin
if(~w_dsi_init_done)
r_lcd_driver_resetn
<= 0;
else
r_lcd_driver_resetn
<= {r_lcd_driver_resetn, 1'b1};
end
lcd_driver
u_lcd_driver
(
// global clock
.clk (w_dsi_clk),
.rst_n (r_lcd_driver_resetn[15]), // w_dsi_rstn),
// Do
not generate timing when init.
// lcd interface
.lcd_dclk (),
.lcd_blank (),
.lcd_sync (),
.lcd_request (w_lcd_request), // Request data 1 cycle
ahead.
.lcd_hs (w_lcd_hs),
.lcd_vs (w_lcd_vs),
.lcd_en (w_lcd_de),
.lcd_rgb ({w_lcd_red2, w_lcd_green2,
w_lcd_blue2, w_lcd_red, w_lcd_green, w_lcd_blue}),
// user interface
.lcd_xpos (w_lcd_xpos), //lcd horizontal coordinate
.lcd_ypos (w_lcd_ypos), //lcd vertical coordinate
.lcd_data ({2{w_lcd_data}})
);
reg [5:0]
r_frame_cnt;
reg [1:0] r_lcd_vs;
always@(posedge w_dsi_clk)begin
r_lcd_vs <= {r_lcd_vs,w_lcd_vs};
if(r_lcd_vs == 2'b01)
r_frame_cnt <= r_frame_cnt + 1'b1;
else
r_frame_cnt <= r_frame_cnt;
end
assign
led = r_frame_cnt[5];
////////////////////////////////////////////////////////////////
// MIPI TX
// Line Width (in pixels) = 1080. There're a
total 1920 lines.
reg
[15:0] r_mipi_tx_line_width = 1080;
// Command Mode
reg
r_mipi_tx_cmd_mode =
0;
// Line Data. Little Endian. Put R at Lsb.
wire
[47:0] w_mipi_tx_line_data = {w_lcd_blue2, w_lcd_green2, w_lcd_red2,
w_lcd_blue, w_lcd_green, w_lcd_red};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DSI Ip
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
wire O_VS_S,O_VS_E;
wire O_HS_S,O_HS_E;
wire O_DATA_EN;
wire [31:0]
O_DATA ;
MIPI_Pixel_to_Byte_Converter_Top
u_pixel_to_byte(
.I_RSTN (w_dsi_init_done), //input I_RSTN
.I_PIXEL_CLK (w_dsi_clk), //input I_PIXEL_CLK
.I_BYTE_CLK (w_dsi_clk), //input I_BYTE_CLK
.I_VSYNC (w_lcd_vs), //input I_VSYNC
.I_HSYNC (w_lcd_hs), //input I_HSYNC
.I_DE (w_lcd_de), //input I_DE
.I_PIXEL (w_mipi_tx_line_data), //input [47:0] I_PIXEL // 2 pixels per cycle
.O_VS_START (O_VS_S), //output O_VS_START
.O_VS_END (O_VS_E), //output O_VS_END
.O_HS_START (O_HS_S), //output O_HS_START
.O_HS_END (O_HS_E), //output O_HS_END
.O_DATA_EN (O_DATA_EN), //output O_DATA_EN
.O_DATA (O_DATA) //output [31:0] O_DATA
);
wire [1:0]
w_lp_clk;
wire [1:0]
w_lp_data0;
wire w_hs_clken;
wire w_hs_dataen;
wire [7:0]
w_hs_clk;
wire [7:0]
w_hs_data0;
wire [7:0]
w_hs_data1;
wire [7:0]
w_hs_data2;
wire [7:0]
w_hs_data3;
localparam WC_REF
= 16'd3240 ; //! 1080*3 = 3072;
localparam DT_RGB
= 6'h3E ;
// Interface Signals
wire
[7:0] w_mipi_tx_lvsc_o;
wire
w_mipi_tx_lvsc_oe;
wire
[31:0] w_mipi_tx_lvsd_o;
wire
w_mipi_tx_lvsd_oe;
wire
w_mipi_tx_lpscp_o,
w_mipi_tx_lpscn_o;
wire
[3:0] w_mipi_tx_lpsdp_o, w_mipi_tx_lpsdn_o;
wire
w_mipi_tx_lpscp_oe,
w_mipi_tx_lpscn_oe;
wire
[3:0] w_mipi_tx_lpsdp_oe, w_mipi_tx_lpsdn_oe;
MIPI_DSI_CSI2_TX_Top
u_MIPI_DSI_CSI2_TX(
.I_RSTN (w_dsi_init_done), //input I_RSTN
.I_BYTE_CLK (w_dsi_clk), //input I_BYTE_CLK
.I_VS_START (O_VS_S), //input I_VS_START
.I_VS_END (O_VS_E), //input I_VS_END
.I_HS_START (O_HS_S), //input I_HS_START
.I_HS_END (O_HS_E), //input I_HS_END
.I_WC (WC_REF), //input [15:0] I_WC
.I_VC (2'b00), //input [1:0] I_VC
.I_DT (DT_RGB), //input [5:0]
I_DT
.I_DATA_EN (O_DATA_EN), //input I_DATA_EN
.I_DATA (O_DATA), //input [31:0] I_DATA
.O_LP_CLK ({w_mipi_tx_lpscp_o,
w_mipi_tx_lpscn_o}), //output [1:0] O_LP_CLK
.O_HS_CLK_EN (w_mipi_tx_lvsc_oe), //output O_HS_CLK_EN
.O_HS_CLK (w_mipi_tx_lvsc_o), //output [7:0] O_HS_CLK
.O_LP_DATA0 ({w_mipi_tx_lpsdp_o[0],
w_mipi_tx_lpsdn_o[0]}), //output
[1:0] O_LP_DATA0
.O_HS_DATA_EN (w_mipi_tx_lvsd_oe), //output
O_HS_DATA_EN
.O_HS_DATA0 (w_mipi_tx_lvsd_o[0*8+:8]), //output [7:0] O_HS_DATA0
.O_HS_DATA1 (w_mipi_tx_lvsd_o[1*8+:8]), //output [7:0] O_HS_DATA1
.O_HS_DATA2 (w_mipi_tx_lvsd_o[2*8+:8]), //output [7:0] O_HS_DATA2
.O_HS_DATA3 (w_mipi_tx_lvsd_o[3*8+:8]) //output [7:0] O_HS_DATA3
);
assign
w_mipi_tx_lpsdp_o[3:1] = {3{w_mipi_tx_lpsdp_o[0]}};
assign
w_mipi_tx_lpsdn_o[3:1] = {3{w_mipi_tx_lpsdn_o[0]}};
// Attach BUSY flag to HS_EN.
//assign
w_dsi_init_busy = w_mipi_tx_lvsd_oe;
// Debug
wire
[7:0] w_mipi_tx_lvsd_0, w_mipi_tx_lvsd_1, w_mipi_tx_lvsd_2,
w_mipi_tx_lvsd_3;
assign
{w_mipi_tx_lvsd_3, w_mipi_tx_lvsd_2, w_mipi_tx_lvsd_1, w_mipi_tx_lvsd_0} =
w_mipi_tx_lvsd_o;
//
---------------------------MIPI DSI发送模块
mipi_tx_0
mipi_tx_inst (
.sclk (w_dsi_clk), //input sclk
.reset_n (w_dsi_rstn), //input reset_n
.clk_bit (w_dsi_pll_0), //input clk_bit
.clk_bit_90 (w_dsi_pll_90), //input clk_bit_90
.MIPI_CLK_P (TX_CLK_P), //inout MIPI_CLK_P
.MIPI_CLK_N (TX_CLK_N), //inout MIPI_CLK_N
.MIPI_LANE3_P (TX_DATA_P[3]), //inout MIPI_LANE3_P
.MIPI_LANE3_N (TX_DATA_N[3]), //inout MIPI_LANE3_N
.MIPI_LANE2_P (TX_DATA_P[2]), //inout MIPI_LANE2_P
.MIPI_LANE2_N (TX_DATA_N[2]), //inout MIPI_LANE2_N
.MIPI_LANE1_P (TX_DATA_P[1]), //inout MIPI_LANE1_P
.MIPI_LANE1_N (TX_DATA_N[1]), //inout MIPI_LANE1_N
.MIPI_LANE0_P (TX_DATA_P[0]), //inout MIPI_LANE0_P
.MIPI_LANE0_N (TX_DATA_N[0]), //inout MIPI_LANE0_N
.lp_clk_out ({w_mipi_tx_lpscp_o,
w_mipi_tx_lpscn_o}), //input [1:0] lp_clk_out
.lp_clk_dir (1),//w_mipi_tx_lvsc_oe ? 0 :
w_mipi_tx_lpscp_oe), //input
lp_clk_dir // 1:LP-Out. 0:LP-In.
.lp_clk_in (), //output
[1:0] lp_clk_in
.clk_data (w_mipi_tx_lvsc_o), //input [7:0] clk_data
.lp_data3_out({w_mipi_tx_lpsdp_o[3], w_mipi_tx_lpsdn_o[3]}),
//input [1:0] lp_data3_out
.lp_data3_dir(1),//w_mipi_tx_lvsd_oe ? 0 :
w_mipi_tx_lpsdp_oe[3]), //input
lp_data3_dir
.lp_data3_in (), //output
[1:0] lp_data3_in
.data_in3 (w_mipi_tx_lvsd_o[3*8+:8]), //input [7:0] data_in3
.lp_data2_out({w_mipi_tx_lpsdp_o[2], w_mipi_tx_lpsdn_o[2]}),
//input [1:0] lp_data2_out
.lp_data2_dir(1),//w_mipi_tx_lvsd_oe ? 0 :
w_mipi_tx_lpsdp_oe[2]), //input
lp_data2_dir
.lp_data2_in (), //output
[1:0] lp_data2_in
.data_in2 (w_mipi_tx_lvsd_o[2*8+:8]), //input [7:0] data_in2
.lp_data1_out({w_mipi_tx_lpsdp_o[1], w_mipi_tx_lpsdn_o[1]}),
//input [1:0] lp_data1_out
.lp_data1_dir(1),//w_mipi_tx_lvsd_oe ? 0 :
w_mipi_tx_lpsdp_oe[1]), //input
lp_data1_dir
.lp_data1_in (), //output
[1:0] lp_data1_in
.data_in1 (w_mipi_tx_lvsd_o[1*8+:8]), //input [7:0] data_in1
// Use Data 0 LP mode configuration. When
w_dsi_init_done, switch back.
.lp_data0_out(w_dsi_init_done ? {w_mipi_tx_lpsdp_o[0],
w_mipi_tx_lpsdn_o[0]} : {w_dsi_tx_lp_p_o, w_dsi_tx_lp_n_o}), //input [1:0] lp_data0_out
.lp_data0_dir(1),//w_mipi_tx_lvsd_oe ? 0 :
w_mipi_tx_lpsdp_oe[0]), //input
lp_data0_dir
.lp_data0_in (), //output
[1:0] lp_data0_in
.data_in0 (w_mipi_tx_lvsd_o[0*8+:8]), //input [7:0] data_in0
.hs_clk_en (w_mipi_tx_lvsc_oe), //input hs_clk_en
.hs_data_en (w_mipi_tx_lvsd_oe) //input hs_data_en
);
//assign
dsi_reset_o = w_dsi_rstn;
// PWM Must be <= 40.
PWM
dsi_pwm (.C(w_dsi_clk), .I(255), .O(dsi_pwm_o));
endmodule

步骤 5:创建和添加其它文件
创建和添加时钟模块、彩条生成模块、LCD 控制模块、MIPI DSI 发送模块等相关文件,最后设计文件中包括以下Verilog设计文件。

三、创建约束
引脚约束文件
点击工程窗口中的 "Files" →
"New" → "Physical Constraints File"

-
文件名输入:mipi_color_bar_1920_1080.cst,点击
"OK"
-
输入以下约束(针对 VF-G60K225 开发板):
IO_LOC "TX_DATA_P[0]" B14,A14; [
IO_PORT "TX_DATA_P[0]"
IO_TYPE=MIPI PULL_MODE=NONE DRIVE=2 BANK_VCCIO=1.2;
IO_LOC "TX_DATA_P[1]" F8,E9;
IO_PORT "TX_DATA_P[1]"
IO_TYPE=MIPI PULL_MODE=NONE DRIVE=2 BANK_VCCIO=1.2;
IO_LOC "TX_DATA_P[2]" E7,E8;
IO_PORT "TX_DATA_P[2]"
IO_TYPE=MIPI PULL_MODE=NONE DRIVE=2 BANK_VCCIO=1.2;
IO_LOC "TX_DATA_P[3]" B13,A13;
数据通道3
IO_PORT "TX_DATA_P[3]"
IO_TYPE=MIPI PULL_MODE=NONE DRIVE=2 BANK_VCCIO=1.2;
IO_LOC "TX_CLK_P" E5,E6;
IO_PORT "TX_CLK_P" IO_TYPE=MIPI
PULL_MODE=NONE DRIVE=2 BANK_VCCIO=1.2;
IO_LOC "dsi_pwm_o" K3;
IO_PORT "dsi_pwm_o"
IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "dsi_reset_o" K4;
IO_PORT "dsi_reset_o"
IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "led" H13;
IO_PORT "led" IO_TYPE=LVCMOS33
PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "clk_25m" L6;
IO_PORT "clk_25m"
IO_TYPE=LVCMOS33 PULL_MODE=NONE BANK_VCCIO=3.3;
- 按Ctrl+S保存文件

Timing 约束
-
点击工具栏中的 "Tools-Timing Constraints Editor" 按钮
-
在弹出的 "Synthesis" 窗口中保持默认设置
-
点击 "Start" 开始综合
-
等待综合完成,确保没有错误(查看底部消息窗口)

第一进入时,提示没有这个文件,选择OK,创建这个文件。

进入以下界面,

选择菜单Constraints—Create Clock

按以下配置,如图,

点击OK,配置完成后,是这样的,

点击Timing Constraints Files,看到的文件如图,

四、综合设计
- 点击工具栏中的 "Synthesize" 按钮(图标为蓝色箭头)
- 等待综合完成,确保没有错误(查看底部消息窗口)

五、布局布线
- 点击工具栏中的 "Run Place&Route" 按钮(图标为绿色箭头)
- 等待完成,确保没有错误

六、连接开发板并下载程序
1 使用 USB 线缆连接 VF-G60K225 开发板到电脑
2 确保开发板已上电
3 点击工具栏中的 "Programmer" 按钮(图标为 数字+向下箭头)

4 点击Save;
5 检查设备Device为GW5AT,Operation为SRAM Program;FS File为工程目录里的fs文件。点击“Programming/Configure”按钮,开始烧录,

6 等待下载完成,显示Finished和 烧录所用时长。
七、验证结果
-
烧录结束后,
在VD-MIPI-5.5TH LCD 屏幕上会显示出代码所设计的图案;
在本次实验中,我们设计的图案是与LCD点位置坐标相关的图案,

它生成的图案如下,

对图案生成函数稍微修改一下,

新生成的的图案如下,

按照以上步骤操作,即可在高云 Arora V-60K 开发板上实现 VD-MIPI-5.5TH LCD 屏幕的显示测试。
如果遇到问题,可以查看软件底部的消息窗口获取详细错误信息,针对性地解决问题。