VGA IP Core 设计与实现
Comedian
June 1, 2011
声声声明明明
本文章所有内容只是个人理解及个人的实现方法,可能存在诸多错误
撰写本文的主要目的是记录自己的学习过程,并与他人分享
Abstract
最近在做 OpenSOC 平台的搭建以及 Linux 的移植。 OpenSOC 的平台已
经基本实现,建立了 Verilog 工程,成功地在 FPGA 实验箱上运行。后来
需要进行 Linux 移植,由于原本的 Linux 启动信息都是从串口输出,一
般我们是把实验箱上面的串口接至计算机,用串口调试助手之类的工具看
到 Linux 的启动信息。
后来我产生了一个想法,能不能把串口出来的信息,转换成 VGA 信
号从实验箱上的 VGA 口输出,驱动液晶显示器,以从显示器上观察
到 Linux 的启动信息呢?
构思了一段时间后,决定写一个这样的 VGA IP Core :在液晶显示器
上能够显示数字、大小写字母、以及加减乘除感叹号等符号,所显示的数
据采用 ASCII 码的形式编码。将字母、数字或者标点符号对应的 ASCII 码
作为 IP Core 的数据输入,给一个使能信号后,即将该字母、数字或者符
号从显示器上显示。能够换行,当一屏写满后能够向上卷动以继续显示后
面的数据。
经过接近一个星期的努力,最终终于实现了这个功能。但是由于时间以
及经验的不足,所设计的 IP Core 还有点缺陷,但是基本的功能以实现,
至于 IP Core 的维护以及更新打算在以后逐步完成。
Contents
1 VGA 时时时序序序以以以及及及如如如何何何产产产生生生 3
1.1 VGA接口 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 VGA驱动原理 . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 时序产生以及颜色给定 . . . . . . . . . . . . . . . . . . . . . . 4
1.3.1 行同步信号以及场同步信号怎么给 . . . . . . . . . . . 4
1.3.2 屏幕上的每个像素点是如何给定颜色的 . . . . . . . . . 6
2 字字字模模模提提提取取取 9
2.1 使用PCtoLCD2002提取字模 . . . . . . . . . . . . . . . . . . . 9
2.2 mif文件格式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 用C代码把从PCtoLCD软件得到的数据转换成mif文件 . . . . 10
3 VGA IP Core设设设计计计 11
3.1 屏幕上的像素以及字符 . . . . . . . . . . . . . . . . . . . . . . 11
3.2 显示字符的原理 . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.3 实现向上卷动显示 . . . . . . . . . . . . . . . . . . . . . . . . 14
3.4 遇到换行怎么办 . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4.1 简单的处理办法(不完美) . . . . . . . . . . . . . . . . 16
3.4.2 更加完美的换行处理 . . . . . . . . . . . . . . . . . . . 17
3.5 如何使用这个VGA IP Core . . . . . . . . . . . . . . . . . . . 17
4 UART 模模模块块块 19
4.1 系统时钟、波特率 . . . . . . . . . . . . . . . . . . . . . . . . 19
4.2 如何通过UART发送数据 . . . . . . . . . . . . . . . . . . . . . 19
4.3 采样时钟的概念以及如何通过UART接受数据 . . . . . . . . . 20
4.4 如何产生采样时钟 . . . . . . . . . . . . . . . . . . . . . . . . 20
4.5 还缺什么 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1
CONTENTS 2
5 未未未来来来要要要做做做什什什么么么 23
5.1 一个完美的VGA IP Core . . . . . . . . . . . . . . . . . . . . 23
5.2 我的数字设计水平 . . . . . . . . . . . . . . . . . . . . . . . . 23
5.3 所以,未来还有很长的路. . . . . . . . . . . . . . . . . . . . . . 23
A 生生生成成成mif文文文件件件的的的C代代代码码码 24
B 效效效果果果图图图 26
Chapter 1
VGA 时时时序序序以以以及及及如如如何何何产产产生生生
本章节主要是介绍 VGA 时序,以及如何用 Verilog 实现。VGA 时序部
分我想很多人比我了解的更加清晰,我也是从网络上各种高手那里学习而
得,但是可能仍然有的朋友之前没有接触过,所以我还是简单记录一下。
更加详细的资料,可以看一下akuei2 写的《Verilog HDL 建模技巧- 低级建
模之VGA全驱动》
1.1 VGA接接接口口口
Fig. 1.1: VGA in Fig. 1.2: VGA out Fig. 1.3: VGA pin
3
CHAPTER 1. VGA 时序以及如何产生 4
1.2 VGA驱驱驱动动动原原原理理理
由图Fig.1.3可以看出,虽然有VGA接口有15个引脚,但是主要还是用其
中的5个引脚。就像使用串口的时候,RS232接口有9个引脚,但是基本只
用3个引脚(GND、RXD、TXD)。
VGA 驱动的原理大概如下:
给引脚 Hsync 一定的行同步信号,同时再给 Vsync 一定的场同步信
号,那么 VGA 信号就从第一行第一个像素(屏幕最左上角)开始扫描,直
到最后一行最后一个像素(屏幕最右下角)。如果扫描到屏幕上某个像素
的时候,Red、Green、Blue 引脚上恰好是某个模拟电压,那么屏幕上的这
个像素就显示这个模拟电压对因的颜色。
那么 VGA 驱动主要分为2个方面:
1.3 时时时序序序产产产生生生以以以及及及颜颜颜色色色给给给定定定
1.3.1 行行行同同同步步步信信信号号号以以以及及及场场场同同同步步步信信信号号号怎怎怎么么么给给给
先根据Fig.1.51决定使用哪个显示模式,比如要实现800×600@60Hz,
就是指在屏幕上显示800列、600行,60Hz代
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
每秒扫描60屏。此时 clk 要
采用40Mhz。然后再参照Fig.142,a段给128个clk,b段给88个clk,c段给800
个clk,d段给40个clk,那么一个行同步信号就产生了;然后o段给4个行同
步信号,p段给23个行同步信号,q段给600个行同步信号,r段给1个行同步
信号,这样一个场同步信号就产生了。
用Verilog实现上述信号的基本思路就是用clk计数产生行信号,用行信号
计数产生场信号。
当行信号计数到达c,场信号计数到q段时,就是对应屏幕中的某个点
了。例如,行计数到了c段的第400,场计数到了q段的第500,此时代表屏
幕的第500行,第400列那个像素,如果此时Red、Green、Blue给定了某个
电压值,那么该点就显示了对应的颜色。
1Fig.1.5摘自 www.oshbbs.com 的《VGA驱动及实现》
2Fig.1.3摘自akuei2写的《 Verilog HDL 建模技巧- 低级建模之 VGA 全驱动》
CHAPTER 1. VGA 时序以及如何产生 5
Fig. 1.4: 同步信号
Fig. 1.5: 显示
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
CHAPTER 1. VGA 时序以及如何产生 6
1.3.2 屏屏屏幕幕幕上上上的的的每每每个个个像像像素素素点点点是是是如如如何何何给给给定定定颜颜颜色色色的的的
Red、Green、Blue引脚的给的模拟电压范围是0~0.714V,如果R、G、
B都给了0.714V,那么该点就显示白色,如果R、G、B都给了0V,那么该
点就显示黑色,如果R、G、B分别给了0.714V、0V、0V,那么该点就显示
红色。
但是FPGA出来的都是数字电平,可以采用图Fig.1.63的方式实现简单
的DAC。
Fig. 1.6: 简单的DAC
这样的话,如果R、G、B都给了4’b1111、4’b1111、4’b1111,那么这个
点就显示白色。下面附上产生行同步信号以及场同步信号的 Verilog 代码
//800 × 600@60Hz
always @ (negedge c l k ) // crea t hsync s i g n a l
begin
i f ( ! r s t n )
begin
hsync cnt <=1;
hsync<=0;
end
else
begin
i f ( hsync cnt == 128)
3Fig.1.6摘自博创科技EDA实验箱的说明
书
关于书的成语关于读书的排比句社区图书漂流公约怎么写关于读书的小报汉书pdf
《UP-CUP FPGA2C35-II平台使用说明书》
CHAPTER 1. VGA 时序以及如何产生 7
begin
hsync<=1;
hsync cnt<=hsync cnt +1;
end
else i f ( hsync cnt == 1056)
begin
hsync cnt <=1;
hsync<=0;
end
else
hsync cnt<=hsync cnt +1;
end
always @ (negedge hsync ) // crea t vsync s i g n a l
begin
i f ( ! r s t n )
begin
vsync cnt <=1;
vsync<=0;
end
else
begin
i f ( vsync cnt == 4)
begin
vsync<=1;
vsync cnt<=vsync cnt +1;
end
else i f ( vsync cnt ==628)
begin
vsync cnt <=1;
vsync<=0;
end
else
vsync cnt<=vsync cnt +1;
end
hsync 信号以及 vsync 信号就这样产生了,很简单。但是要注意只有当
( hsync cnt >=216) && ( hsync cnt <=1016) && ( vsync cnt>=
27) && ( vsync cnt <=627)
CHAPTER 1. VGA 时序以及如何产生 8
的时候,RGB数据才有效。hsync代表行同步信号给 VGA接口的 hsync引
脚,见 Fig.1.4 。vsync 代表场同步信号,给 VGA 接口的 vsync 引脚,
见 Fig.1.4 。
hsync cnt-216表示扫描到了屏幕上的第 hsync cnt-216列,vsync cnt-
27代表扫描到了屏幕上的第 vsync cnt-27行。
Chapter 2
字字字模模模提提提取取取
本文所写的VGA IP Core主要是为了显示字母、数字以及一些符号,所
以需要用字模软件提取字模。提取完字模数据以后,要把那么多的字模数
据一个一个地敲入mif文件中实在太麻烦,于是我写了一个简单的C程序,
实现了将得到的字模数据转换成mif文件,用于Altera rom例化。
2.1 使使使用用用PCtoLCD2002提提提取取取字字字模模模
我使用了PCtoLCD2002字模提取软件提取了小写字母、大写字母以及
一些符号的字模。提取出来的字模是8×16的,就是一个字符是包含16行,8列
个像素。
字模数据格式如下,以大写字母‘H’为例,对应的字模数据是0x00,0x00,
0x00,0xE7,0x42,0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0x42,0xE7,0x00,0x00,
共16个8bit数据。每8bit代表一行。
2.2 mif文文文件件件格格格式式式
先看一下一个简单的mif文件
WIDTH=8;
DEPTH=2;
ADDRESS RADIX=HEX;
DATA RADIX=HEX;
CONTENT BEGIN
0:00;
1:01;
END;
9
CHAPTER 2. 字模提取 10
这是一个简单mif文件,WIDTH=8表示mif文件中每个数据是8bit,DEP
TH=2表示共有2个这样的数据, ADDRESS RADIX=HEX 表示数据的地
址是以16进制描述的, DATA RADIX=HEX 表示是按16进制形式给出
的,CONTENT BEGIN表示数据开始,之后就是数据,按“数据地址:数
据;”这样的形式一一给出,最后以”END;”结束。
2.3 用用用C代代代码码码把把把从从从PCtoLCD软软软件件件得得得到到到的的的数数数据据据转转转换换换成成成mif文文文
件件件
使用fopen函数打开数据源文件和要生成的 mif 文件, fprintf 函数先
向 mif文件中写入WIDTH、 DEEPTH、 ADDRESS RADIX、DATA R
ADIX、 CONTENT BEGIN 等信息,然后使用一个 while 循环从数据源文
件中读,每读到2个字符就向mif文件中写“数据地址:读来的数据;”,直
到 feof (数据源文件)为真的时候从while中break,然后再写入“END;”,
接着关闭数据源文件以及mif文件,程序就完成了。详细的见附录中的代
码。
根据需要,我提取了 ASCII Table 中的第32个字符Space,到第126个字
符~。
数据排放顺序是按照ASCII Table,从第32个字符开始,先放置第32个
字符的字模数据的第0行8bit数据,接着放置第1行8bit数据,直到第15行。
然后再如此放置第33个字符的字模数据,直到第126个字符,结束。
这样放置是为了是VGA IP Core输入对应字符的ASCII值的时候,只要
使用下面这个公式就能得到这个字符对应的字模数据。
(字符的ASCII − 32) ∗ 16 =字符对应字模的第0行数据 (2.1)
(字符的ASCII − 32) ∗ 16 + 1 =字符对应字模的第1行数据 (2.2)
. . . (2.3)
(字符的ASCII − 32) ∗ 16 + 15 =字符对应字模的第15行数据(2.4)
Chapter 3
VGA IP Core设设设计计计
Chapter1以及Chapter2介绍了VGA信号产生以及字模提取,这些都是
比较容易实现的,网络上有很多很多实现方法比我的实现方法更加完善。
后续的章节是本文的重点,当然也可能漏洞百出。
3.1 屏屏屏幕幕幕上上上的的的像像像素素素以以以及及及字字字符符符
下图表示的是一个屏幕最左上角的一部分
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1
0 * * * * * * * * # # # # # # # #
1 * * * * * * * * # # # # # # # #
2 * * * * * * * * # # # # # # # #
3 * * * * * * * * # # # # # # # #
4 * * * * * * * * # # # # # # # #
5 * * * * * * * * # # # # # # # #
6 * * * * * * * * # # # # # # # #
7 * * * * * * * * # # # # # # # #
8 * * * * * * * * # # # # # # # #
9 * * * * * * * * # # # # # # # #
10 * * * * * * * * # # # # # # # #
11 * * * * * * * * # # # # # # # #
12 * * * * * * * * # # # # # # # #
13 * * * * * * * * # # # # # # # #
14 * * * * * * * * # # # # # # # #
15 * * * * * * * * # # # # # # # #
0
1
11
CHAPTER 3. VGA IP CORE设计 12
上图中每一个小格是一个像素点,(hsync cnt-216,vsync cnt-27)表示扫
描到哪个像素点。*部分属于屏幕上第0行第0列的字符,#部分属于屏幕上
第0行第1列的字符。每个字符需要8×16个像素用于显示。
3.2 显显显示示示字字字符符符的的的原原原理理理
为了通过VGA显示字符,我使用了Altera rom以及Altera ram IP核。rom
中例化了字模数据,ram充当显存。每次一个使能信号给VGA IP Core的
时候,就能从VGA显示出数据端口上的数据对应的ASCII字符。VGA IP
Core的模块图见Fig.3.1。 以800×600@60Hz的显示标准说明显示原理。
Fig. 3.1: VGA模块图
由于一个显示一个字符需要16行、8列,那么800×600的显示模式下,
最多一行显示100个字符,最多显示37行。
Screen Data Ram(用Altera Ram实现)
0
1
2
. . .
36
oo
oo
oo
oo
0
1
2
. . .
36
在上图中,data ram中每行有100个数据,共37行,每个数据是8bit的。data
ram充当显存的功能,如果data ram中第12行,第54列中的数据是8’d48,
那么就表示应该在屏幕的上的第12行第54列那个位置处显示Altera rom存储
的第16(48-32=16)个ASCII字符。根据Fig.2.1知道要显示的字符是’0’(注
意rom中存放的第一个字符是Space,所以第16个是’0’)。
在FPGA内部是这样工作的:由于hsync cnt以及vsync cnt一直在计数,
当
CHAPTER 3. VGA IP CORE设计 13
( hsync cnt >=216) && ( hsync cnt <=1016) && ( vsync cnt>=
27) && ( vsync cnt <=627)
的时候,就说明扫描到了屏幕上可显示区域了。举例,如果hsync cnt=237,
vsync cnt=47,就表示扫描到了屏幕上第21(237-216)列、第20(47-27)
行这个像素点上了。先判断这个像素点应该对应显存中的那个字符,21除
以8得2余5,20除以16得1余4,那么就表示应该是屏幕上第2行第1列的那个
字符,然后找到data ram中第2行第1列的那个8bit数据,假设是8’d50,再
根据这个8’d50找到rom中需要显示字符的字模数据,根据公式2.1~2.4,得
到每一行的字模数据,共16行。
简单的说:屏幕上每一个像素点都属于一个字符的字模数据中的一个
点,每扫描到一个(hsync cnt-216,vsync cnt-27)像素点,先判断这个像素点
是属于data ram中哪个位置,这个位置上存放着某个字符的字模号,再根
据这个字模号从rom中读出字模数据,用于显示这个像素点(是白色,还是
黑色)。
当VGA模块刚上电的时候,在data ram中应该没有数据,也就是说
还没有数据输入VGA模块以显示。当我需要在屏幕最左上角,也就是
第1个字符出显示一个字符的时候,首先我把要显示字符的ASCII码值输
入到data input[7:0]上,然后把flag从高拉低产生一个下降沿,此时这个字
符的ASCII码就写入到data ram的第0位置上。之后,每扫描到屏幕上最左
上角的时候,判断此处显示的字符的ASCII码在data ram的第0位置,然后
读取这个ASCII值,再根据这个值从rom中读出对应的字模数据,根据字模
数据,在屏幕最左上角显示这个字符。
之后,如果还需要显示一个字符,继续把要显示字符的ASCII值放
在data input[7:0]上,再拉低flag产生一个下降沿,ASCII值就写入到data
ram的第1位置,. . .。
也就是说:如果想在屏幕上的(x,y)处显示某个字符,只要把这个字
符写到data ram的(x,y)位置;data ram的(x,y)位置上的ASCII值对应屏幕
上(x,y)处的字符。这样就把屏幕与data ram联系在一起了,要想显示,只
要操作data ram即可。data ram也就充当了显存的功能。
如前所说,800×600模式下,一个屏幕最多显示37行,每行100个字
符,也就是3700个字符。当向data ram中写了第3700个字符时,这个字符
就被显示在屏幕的最右下角。到此时,正好写满一屏。
此后还需要显示字符的时候,就又从data ram的第0位置写。但是就出
现一个现象,当显示完一屏后,还要显示的话,就又从屏幕的最左上角显
示了。而正常情况下,是能够向上卷动显示的,就是说显示完一屏后还要
显示,那么原来第0行就消失了,其他行都向上跑了一行,后来显示的数据
在最后一行显示。下面就说明如何实现向上卷动显示。
CHAPTER 3. VGA IP CORE设计 14
3.3 实实实现现现向向向上上上卷卷卷动动动显显显示示示
这一部分的实现方法,用文字很难表达,用图能够清晰地表达我的思
路。
Screen Data Ram(用Altera Ram实现)
0
1
2
. . .
36
oo
oo
oo
oo
0
1
2
. . .
36
还没有显示完一屏的时候屏幕与data ram的对应关系
Screen Data Ram(用Altera Ram实现)
0
1
2
. . .
36 ||
ll
ll
ll
0
1
2
. . .
36
显示完一屏后,继续显示的时候,应该向上卷动一行。需要显示的字
符的ASCII值被写到data ram的第0行,但是这个字符应该显示在屏幕的最
下面,也就是屏幕的第36行。而原来的第0行字符应该不再被显示出来,原
来第1行的字符现在被显示到第0行,第2行的字符被显示到第1行,. . .。这
样屏幕与data ram的对应关系就如上图。这种关系一直持续到写完新的一
行。
Screen Data Ram(用Altera Ram实现)
0
1
2
. . .
36
zz
zz
jj
jj
0
1
2
. . .
36
当继续显示的时候,应该向上卷动2行。对应关系如上图。
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
CHAPTER 3. VGA IP CORE设计 15
Screen Data Ram(用Altera Ram实现)
0
1
2
. . .
36
rr
rr
rr
bb 0
1
2
. . .
36
向上卷动36行后,屏幕与data ram的对应关系如上图。
之后再显示的时候就又出现了
Screen Data Ram(用Altera Ram实现)
0
1
2
. . .
36
oo
oo
oo
oo
0
1
2
. . .
36
这样就出现了一个循环。那么向上卷动的功能就实现了。
根据这些图,可以用归纳法得出屏幕与data ram对应关系:如果没有卷
动的时候,那么屏幕的(x,y)对应data ram的(x,y);如果卷动了一行,那么
屏幕的第36行对应data ram的第0行,屏幕的第0~35行,对应data ram的
第1~36行;. . .;如果卷动了n行,那么屏幕的第(37-n)~36行,对应data
ram的第0~(n-1)行,而屏幕的第0~(36-n)行对应data ram的第n~36行。V
erilog代码如下
always @ (negedge c l k )
begin
i f ( cur r ent char y <=(36− c h a r c n t s t a r t ) )
begin
char cnt<=
( c u r r e n t c h a r y+c h a r c n t s t a r t )∗100+ c u r r e n t c h a r x ;
end
else
begin
char cnt<=
( cur rent char y−37+c h a r c n t s t a r t )∗100+ c u r r e n t c h a r x ;
end
end
\\ 说明: c h a r c n t s t a r t 表示卷动了多少行, char cnt 表示此
\\ 时显示 data ram 上的那个字符的位置, c u r r e n t c h a r y 表示
CHAPTER 3. VGA IP CORE设计 16
\\ 屏幕上那个字符的行数, c u r r e n t c h a r x 表示屏幕上那个字符的
\\ 列数。
3.4 遇遇遇到到到换换换行行行怎怎怎么么么办办办
3.4.1 简简简单单单的的的处处处理理理办办办法法法(不不不完完完美美美)))
由前所述,当flag出现下降沿的时候,应该把data input[7:0]上的数据写
到data ram上。那么就在每次flag出现下降沿的时候,先判断data input[7:0]
是不是换行,也就是说是不是13(换行的ASCII值),如果不是,那么就正
常写入到data ram下一个位置。如果是换行,那么表示下一次的字符应该
被写入到data ram的下一行。可以用下面的代码实现
i f ( data input == 13)
begin
d i s x <=0;// d i s x 表示写到 data ram 的列数
i f ( d i s y == 36) // d i s y 表示写到 data ram 的列
begin
d i s y <=0;
end
else
begin
d i s y<=d i s y +1;
end
end
但是这样会出现一个情况:假设FPGA上电了,我们想用VGA模块显示
一些字符,比如‘0 1 2 3 4 5’,这些字符的ASCII值是存储在data ram的
第0行前6列上,而data ram的第0行的后94列是没有数据的。这样屏幕上
第0行显示的是‘0 1 2 3 4 5 ’,然后回车,这样的话下次的字符
的ASCII值被存放在data ram的第1行。然后继续显示。当一屏写满后,再
想显示字符的时候,字符的ASCII又被写入到data ram的第0行,依次覆盖
原来data ram第0行的‘0 1 2 3 4 5’。假设我们想继续显示‘7 8 9’,那
么此时data ram的第0行的数据有‘7 8 9 3 4 5 ’,也就是说后来写入的数
据‘7 8 9’把原来的‘0 1 2’覆盖了,而后面的‘3 4 5’没有覆盖上,
此时再回车,那么后来的数据将被写入到data ram的第1行。而data ram的
第0行一直保持着‘7 8 9 3 4 5’,那么屏幕上第0行显示的是‘7 8 9 3 4 5
’。然而我们想显示的是‘7 8 9’。
简单地说就是:之前的数据没有被清除,导致后来显示的时候,把之前
应该早已清除的数据显示出来了。
CHAPTER 3. VGA IP CORE设计 17
有一种解决方法是:每次换行的时候,不仅dis y <= dis y + 1(即换
行),为了使后来的字符ASCII值写入到data ram的下一行,还要在换行
的时候,把这一行后面的数据全部清除,然后再dis y <= dis y + 1(换
行)。
可是这样有些麻烦,下面介绍一下我的思路,加上一个小设置,就能轻
松实现相同的功能。
3.4.2 更更更加加加完完完美美美的的的换换换行行行处处处理理理
为每一行添加一个参数dis end per line,这个参数表示这一行的data
ram被写了多少字符,或者这一行的屏幕需要显示多少个字符。例如,我
们想在屏幕第0行显示‘0 1 2 3 4’5个字符,那么我给写入了这5个字符
后,就回车,回车时把第0行的这个参数设置为5。同时扫描像素点显示的
时候,判断有没有超过5,超过了就显示白色,也就是不显示。
这样就能够解决3.4.1中换行处理时出现的问题。
同样的例子:FPAG上电了,我们想在屏幕第0行显示‘0 1 2 3 4 5’
这6个字符,那么我们就依次向data input[7:0]上写入ASCII值48、49、50. . .,
然后回车,同时把第0行的dis end per line设置为6,那么第0行我们只显示
前6个字符,后面的94的字符全部显示白色。然后显示完一屏了,还想继
续显示‘7 8 9’,那么此时‘7 8 9’的ASCII值依次写入到data ram的
第0行,然后回车,第0行的dis end per line被设置成了3。此时data ram的
第0行中的数据是‘7 8 9 3 4 5 ’,后来输入的‘7 8 9’只覆盖了data
ram第0行的前3个字符,后面的字符不变。然而第0行的dis end per line是3,
也就是说显示的时候只显示第0行的前3个字符,虽然data ram第3个位置之
后仍然后字符数据,但是不会再被显示。
这种换行处理很简单,代码很短,但是消耗LE。因为共37行,每行需
要一个dis end per line,每个dis end per line至少7bit(因为每行有100个
字符,当写满一行后,再写下一行的时候,上一行的dis end per line应该被
设置成100)。而我是用reg [6:0] dis end per line [36:0]实现的,这样就很消
耗LE了。
3.5 如如如何何何使使使用用用这这这个个个VGA IP Core
不写不知道,第一次这么正式地写自己的学习记录文档,发现自己的语
文水平实在太差了。本章主要介绍了我是如何设计VGA IP Core以实现期
望的功能的。有很多地方说的很差,表达不清楚,我想可能只有我才能理
解。但是我认为一些很重要的地方,例如向上卷动显示的实现、以及换行
的处理我已经解释清楚了。
即使不理解我的实现方法,也无所谓。有需要的朋友可以直接使用就行
了。
CHAPTER 3. VGA IP CORE设计 18
下面说说使用方法:
参照模块图,如果要显示一个字符,那么只要将该字符的ASCII值给
到data input[7:0]上,然后通过flag产生一个下降沿,屏幕上就显示了这个
字符。显示顺序是从屏幕最左上角开始显示,向右直到屏幕最右下角。例
如要显示‘A B C D’,那么只要先把A的ASCII值给到data input[7:0]上,
从flag产生一个下降沿,再处理B,再处理C,在处理空格,在处理D,即
可。
如果data input[7:0]值是8’d13,就是遇到了换行,则从下一行显示。
当data input[7:0]的值不在[32,126]之间,且不为13,那么flag产生下降
沿后,VGA IP Core不做任何动作。
当显示完一个屏后,就开始向上卷动显示。
Chapter 4
UART 模模模块块块
关于UART IP Core的设计本不应该属于本文的内容。但是由于最后我
提供的Verilog工程中包含UART部分,所以我还是介绍一下我设计的UART
IP Core。
4.1 系系系统统统时时时钟钟钟、、、波波波特特特率率率
系统时钟是指给UART IP Core的时钟,这里实验箱上的时钟是50MHz,
我直接把这个时钟送入UART IP Core。波特率是用来表征UART串口通信
速率,如果波特率是115200,就表示发送一个bit需要 1115200 秒。
4.2 如如如何何何通通通过过过UART发发发送送送数数数据据据
UART空闲
一个下降沿
表示有数据要传输
1 1 1 0 0 0 1 0
UART
停止位1
上图是一次典型的UART数据传输,波特率是115200,传送了数据11100010,
其中无论是数据开始位、停止位,还是数据位,发送时所需的时间长度都
是 1115200秒。可见发送这个数据需要10*
1
115200秒。
通过UART发送数据可以按照上图给时序就可以了。
19
CHAPTER 4. UART 模块 20
4.3 采采采样样样时时时钟钟钟的的的概概概念念念以以以及及及如如如何何何通通通过过过UART接接接受受受数数数据据据
接受UART数据要稍微复杂一些,这里就需要采样时钟的概念了。
采样时钟一般是波特率的16倍或者64倍之类的。以16倍为例,表示UART传
送的每bit被进行了16次采样:
每当一个采样时钟沿到来的时候,对UART数据线采样一次。UART上
没有数据传送的时候,一直是高电平,那么没有数据传送的时候,每次采
样的结果都是高电平。一旦采样到了一个低电平,就是采样到了一个数据
帧的开始位,那么就表示有数据来了。此后就准备接受数据,由于每bit采
样了16次,那么根据上图,可以得出,下降沿之后的采样结果是:
0000000000000000 开始位
1111111111111111 第1个数据 1
1111111111111111 第2个数据 1
. . . 第3~5个数据 100
0000000000000000 第6个数据 0
1111111111111111 第7个数据 1
0000000000000000 第8个数据 0
1111111111111111 停止位
对每bit的16次采样,一般把中间采样的结果作为这个bit的值。这里对
每bit进行了16次采样,故可以选择第8次采样作为结果。
4.4 如如如何何何产产产生生生采采采样样样时时时钟钟钟
我把这个UART模块的波特率设为115200,不能改变,除非修改Verilog代
码。当然,一个功能强大的UART IP Core应该具备这样的功能:有波特率
控制字之类的命令,可以在初始化UART模块的时候设置它的波特率。为了
简单起见,我就没有这样做了。
既然波特率是115200,并且我设定了采样时钟是波特率的16倍,那么产
生的采样时钟的频率应该是115200×16。由于我的FPGA实验箱上的时钟输
入是50MHz,所以需要进行27分频。
50000000÷ (115200 ∗ 16) ≈ 27 (4.1)
这属于奇数分频。我参照了网络上的一篇文章《用Verilog语言实现奇数
倍分频电路3分频、5分频、7分频》实现了27分频。Verilog代码如下
module u a r t b i t c l k g e n ( c lk , r s t n , b i t c l k ) ;
input c l k ; //50MHz
input r s t n ;
CHAPTER 4. UART 模块 21
output b i t c l k ; //115200∗16Hz
reg [ 1 0 : 0 ] cnt n ;
reg [ 1 0 : 0 ] cnt p ;
reg b i t c l k n ;
reg b i t c l k p ;
assign b i t c l k = b i t c l k n | b i t c l k p ;
always @( negedge c l k )
begin
i f (˜ r s t n )
begin
b i t c l k n <=1;
cnt n<=1;
end
else
i f ( cnt n == 13)
begin
b i t c l k n <=˜b i t c l k n ;
cnt n<=cnt n +1;
end
else i f ( cnt n == 27)
begin
b i t c l k n <=˜b i t c l k n ;
cnt n<=1;
end
else
cnt n<=cnt n +1;
end
always @(posedge c l k )
begin
i f (˜ r s t n )
begin
b i t c l k p <=1;
cnt p<=1;
end
else
CHAPTER 4. UART 模块 22
i f ( cnt p == 13)
begin
b i t c l k p <=˜b i t c l k p ;
cnt p<=cnt p +1;
end
else i f ( cnt p == 27)
begin
b i t c l k p <=˜b i t c l k p ;
cnt p<=1;
end
else
cnt p<=cnt p +1;
end
endmodule
4.5 还还还缺缺缺什什什么么么
通过前几小节,已经能够实现收发UART数据了,采样时钟也能够产生
了,一个UART模块似乎已经完成了。但是仔细一想还缺一个很重要的部
件,那就是 接受中断 。使用过51单片机的朋友都知道,串口接收完一个
数据后,会产生一个中断,可以进入中断处理程序。为了配合我写的VGA
IP Core,这个UART IP Core需要一个接受中断信号flag,当接受完一个数
据后,这个flag应该产生一个下降沿,被VGA IP Core接受,通知VGA IP
Core已经接受完了一个数据,可以拿去显示了。
当然,发送数据要一个flag,通知UART IP Core可以发数据了。
一个完整的模块图如下:
Fig. 4.1: UART IP Core
Chapter 5
未未未来来来要要要做做做什什什么么么
5.1 一一一个个个完完完美美美的的的VGA IP Core
应该有这样一个VGA IP Core:
不仅能够显示字母、数字和标点,还能够显示汉字。当然提取了汉字字
模就能够显示,不过如何控制显示哪个汉字倒是一个较为复杂的问题。应
该需要一个输入法。
最好能有初始化阶段,写入控制字,能控制显示字符的颜色,或者实现
其他功能。
健壮的移植性。
最遗憾的是本文提及的VGA IP Core竟然没有光标、删字符的功能。所
以这个IP Core还远远不够。
5.2 我我我的的的数数数字字字设设设计计计水水水平平平
用一句话总结:老烂了!状态机的设计思想还没有扎根在脑子中,本文
提及的VGA IP Core、UART IP Core都是“稀里糊涂”写出来的,能实现
功能,但是代码的可读性极差。我觉得要是用状态机的设计思路来写,应
该会有很大的改善。
相对于之前,还是有了一些进步。在逐步摆脱C语言那种顺序的程序设
计思路的过程中,我还是前进了一些。不过,对于Verilog综合还是缺少经
验,我觉得一个优秀的数字设计人员应该能够清晰地知道一条语句被综合
成什么电路。时序控制应该被更加严格的关注,不能得过且过。
5.3 所所所以以以,,,未未未来来来还还还有有有很很很长长长的的的路路路. . .
. . .
23
Appendix A
生生生成成成mif文文文件件件的的的C代代代码码码
/∗将数据文件转换成 mif 文件格式∗/
#include ” s t d i o . h”
int main ( )
{
int width ;
int depth ;
int depth cnt ;
int i =0;
int char temp ;
int f l a g ;
FILE ∗ f p s r c ;
FILE ∗ f p d e s ;
i f ( ( f p s r c=fopen ( ”/home/comedian /1 .TXT” , ” r ”))==NULL)
{
p r i n t f ( ”Cann ’ t open the source f i l e \n” ) ;
return 0 ;
}
i f ( ( f p d e s=fopen ( ”/home/comedian/ a s c i i . mif ” , ”w”))==NULL)
{
p r i n t f ( ”Cann ’ t open the a s c i i . mif f i l e \n” ) ;
return 0 ;
}
p r i n t f ( ” Enter the width : ” ) ;
s can f ( ”%d”,&width ) ;
p r i n t f ( ” Enter the depth : ” ) ;
s can f ( ”%d”,&depth ) ;
f p r i n t f ( fp des , ”WIDTH=%d ;\n” , width ) ;
f p r i n t f ( fp des , ”DEPTH=%d ;\n” , depth ) ;
f p r i n t f ( fp des , ”\n” ) ;
f p r i n t f ( fp des , ”ADDRESS RADIX=HEX;\n” ) ;
24
APPENDIX A. 生成MIF文件的C代码 25
f p r i n t f ( fp des , ”DATA RADIX=HEX;\n” ) ;
f p r i n t f ( fp des , ”\n” ) ;
f p r i n t f ( fp des , ”CONTENT BEGIN\n” ) ;
depth cnt =0;
char temp =0;
f l a g =0;
while (1 )
{
f s c a n f ( f p s r c , ”%c” ,&char temp ) ;
i f ( f e o f ( f p s r c ) )
break ;
else i f ( ( char temp>=48 && char temp<=57) | | ( char
temp>=65 && char temp <=90) | | ( char temp>=97
&& char temp<=122))
{
i f ( f l a g ==0)
{
f p r i n t f ( fp des , ”%x : ” , depth cnt ) ;
f p r i n t f ( fp des , ”%c” , char temp ) ;
}
else
{
depth cnt++;
f p r i n t f ( fp des , ”%c” , char temp ) ;
f p r i n t f ( fp des , ” ;\n” ) ;
}
f l a g =( f l a g +1)%2;
}
}
f p r i n t f ( fp des , ”END; ” ) ;
f c l o s e ( f p s r c ) ;
f c l o s e ( f p d e s ) ;
return 0 ;
}
Appendix B
效效效果果果图图图
26
APPENDIX B. 效果图 27
APPENDIX B. 效果图 28