首页
  • GM-3568JHF
  • M4-R1
  • M5-R1
  • SC-3568HA
  • M-K1HSE
  • CF-NRS1
  • CF-CRA2
  • 1684XB-32T
  • 1684X-416T
  • C-3568BQ
  • C-3588LQ
  • GC-3568JBAF
  • C-K1BA
商城
  • English
  • 简体中文
首页
  • GM-3568JHF
  • M4-R1
  • M5-R1
  • SC-3568HA
  • M-K1HSE
  • CF-NRS1
  • CF-CRA2
  • 1684XB-32T
  • 1684X-416T
  • C-3568BQ
  • C-3588LQ
  • GC-3568JBAF
  • C-K1BA
商城
  • English
  • 简体中文
  • GM-3568JHF

    • 一、简介

      • GM-3568JHF 简介
    • 二、快速开始

      • 01 环境搭建
      • 02 编译说明
      • 03 烧录指南
      • 04 调试工具
      • 05 软件更新
      • 06 查看信息
      • 07 测试命令
      • 08 应用编译
      • 09 源码获取
    • 三、外设与接口

      • USB
      • 显示与触摸
      • 以太网
      • WIFI
      • 蓝牙
      • TF-Card
      • 音频
      • 串口
      • CAN
      • RTC
    • 四、应用开发

      • 01 UART读写案例
      • 02 按键检测案例
      • 03 LED灯闪烁案例
      • 04 MIPI屏幕检测案例
      • 05 读取 USB 设备信息案例
      • 06 FAN 检测案例
      • 07 FPGA FSPI 通信案例
      • 08 FPGA DMA 读写案例
      • 09 GPS调试案例
      • 10 以太网测试案例
      • 11 RS485读写案例
      • 12 FPGA IIC 读写案例
      • 13 PN532 NFC读卡案例
      • 14 TF卡读写案例
    • 五、QT开发

      • 01 ARM64交叉编译器环境搭建
      • 02 QT 程序加入开机自启服务
    • 六、FPGA开发

      • ARM与FPGA通讯
      • FPGA开发手册
    • 七、其他

      • 01 根目录文件系统的修改
      • 02 系统自启服务
    • 八、资料下载

      • 资料下载

FPGA开发手册

1 紫光同创IP core的使用及添加

1.1 实验简介

实验目的:了解PDS软件如何安装IP、使用IP以及查看IP手册

实验环境: Window11 PDS2022.2-SP6.4

芯片型号: PG2L50H-484

1.2 实验原理

1.2.1 IP的安装

PDS软件安装完成之后,PDS自带部分基础IP,其他IP需用户下载IP安装包并安装IP。

rootfile1

打开PDS后,点击上图红框部分的IP图标

rootfile1

之后在弹出的选项卡的左上角点击File->Update...

rootfile1

点击左上角Add Package。

rootfile1

如上图是PCIE IP的安装文件,后缀都是**.iar**。大家选择对应的文件后,点击右下角的Open即可。之后勾选上前面的√, 点击 Install即可。

rootfile1

之后在左边的界面可以看到刚才安装的IP即可。注意如果发现安装后,弹出了警告,并且左边的界面没有任何变化。那就意味着你安装的IP该系列的器件不支持。因为你的工程可能是LOGOS、LOGOS2、或者Tian2等系列,不同芯片型号所用的IP是不太相同的,所以大家注意这一点。

1.2.2 例化IP及查看IP手册

rootfile1

继续点击上图所示红框的图标。

rootfile1

选择想要生成的IP,这里以FIFO为例子,即红框1所示。红框2是用来填写生成的IP的名字。点击红框3后即可生成IP,并弹出该IP的配置界面。如下图所示:

rootfile1

其弹出的提示是询问我们是否要把该IP添加到工程中,点击YES就行。如果我们不知道IP如何使用,可以打开官方参考手册查看,如下图所示:

rootfile1

选择想要查看的IP,如何点击红框1所示的图标,即可自动弹出官方参考文档。

rootfile1

对我们的IP配置完成后,点击左上角红框1处的Generate即可。

rootfile1rootfile1

没有任何错误测表示生成成功。

rootfile1

同时工具也会自动弹出一个IP的例化模板,供我们使用。只需要把该例化模板添加到自己的工程之中,即可使用我们生成的IP。

2 键控LED实验

2.1 实验简介

实验目的: 从创建工程到编写代码,完成引脚约束,最后生成bit流下载到开发板上,完成Key0控制led0闪烁,Key1控制led1亮灭。

实验环境: Window11 PDS2022.2-SP6.4

芯片型号: PG2L50H-484

2.2 实验原理

通常的时,分,秒的计时进位大家应该不陌生;

1 小时=60 分钟=3600 秒,当时针转动 1 小时,秒针跳动 3600 次;

在数字电路中的时钟信号也是有固定的节奏的,这种节奏的开始到结束的时间,我们通常称之为周期(T)。

rootfile1

在数字系统中通常关注到时钟的频率,那频率与周期的关系如下:

f=1/T

而本次开发板上的晶振提供了一个25MHZ的单端时钟。

所以其周期约为40ns。而在我们FPGA的设计中,我们的always块通常都是在时钟的上升沿时对数据进行赋值,因此我们可以定义一个变量,每到时钟的上升沿就让该变量+1,让其变成一个计数器,该变量每加1就表示经过了40ns,那如果要定时1s的话,只需要让其计数到24999999即可,因为从0开始计数,所以计数到24999999即可,此时就是一秒了。以此类推,12499999就是0.5s。

rootfile1

上图为开发板上2个LED灯的原理图。

rootfile1

上图为开发板上2个按键的原理图。

KEY0控制LED0每1s更换一次LED灯状态,KE1控制LELD1亮灭状态。(高电平用1表示,低电平用0表示)

2.3 接口列表

top.v 顶层模块接口列表:

端口I/O位宽描述
sys_clkinput1系统时钟 25MHZ
key0input1用户按键0
key1input1用户按键1
led_0output1led灯控制信号
led_1output1led灯控制信号

btn_deb_fix.v 按键消抖模块接口列表:

端口I/O位宽描述
BTN_WIDTHparameter4案件数量
sys_clkinput1系统时钟 25MHZ
rst_ninput1全局复位
btn_ininputBTN_WIDTH用户按键输入
btn_deb_fixoutputBTN_WIDTH按键消抖后的输出(脉冲信号)

2.4.工程说明

该工程框架如下所示:

rootfile1

本次工程主要完成按键控制led的状态。按键0控制led0闪烁,按键1控制led1亮灭。

首先,key0和key1均会经过按键消抖模块,因为开发板上使用的是机械按键,所以每次按下均会产生抖动,如果不进行消抖,会造成误判。经过消抖后,每次按下按键均会产生持续一个clk的高电平即key0_flag和key1_flag。key0_flag控制是否打开1s计数器来开启led0的闪烁,key1_flag直接控制LED1翻转,每按下一次key1,led的状态翻转一次。

2.5 代码模块说明

//key0->led0	闪烁				
//key1->key1	翻转				
module	top(				
input	wire	sys_clk	","	//系统时钟25MHZ	
input	wire	key0	","		
input	wire	key1	","		
					
output	reg	led_0	","		
output	reg	led_1			
);					
//----------------------------------parameter----------------------------------------					
parameter	CNT_MAX	=	32'd25_000_000	;	//1s计数
					
//----------------------------------reg----------------------------------------------					
reg	[7:0]	rsn_cnt	0	;	//复位计数器
reg	[31:0]	cnt_1s	;	//计数器	
reg	led0_en	;	//led0闪烁使能		
					
//----------------------------------wire----------------------------------------------					
wire	rst_n	;	"//复位信号,低电平有效"		
wire	key0_flag	;	//按键按下后的上升沿		
wire	key1_flag	;	//按键按下后的上升沿		
					
//----------------------------------always	&	assign----------------------------------------------			
//产生复位					
always@(posedge	sys_clk)	begin			
if(rsn_cnt	>=100)				
rsn_cnt	<=	rsn_cnt;			
else					
rsn_cnt	<=	rsn_cnt	+	1'b1;	
end					
assign	rst_n	=	(rsn_cnt>=100)?1'b1:1'b0	;	
					
//每按下一次key0进行一次翻转					
always@(posedge	sys_clk)	begin			
if(!rst_n)					
led0_en	<=	1'd0;			
else	if(key0_flag)				
led0_en	<=	~led0_en;			
end					
					
//计数1s					
always@(posedge	sys_clk)	begin			
if(!rst_n)					
cnt_1s	<=	32'd0;			
else	if(led0_en)	//led0闪烁使能			
begin					
if(cnt_1s	==	CNT_MAX-1)	//1秒		
cnt_1s	<=	32'd0;			
else					
cnt_1s	<=	cnt_1s	+	1'b1;	
end					
else					
cnt_1s	<=	32'd0;			
end					
					
//led0	1s闪烁				
always@(posedge	sys_clk)	begin			
if(!rst_n)					
led_0	<=	1'd0;			
else	if(led0_en)				
begin					
if(cnt_1s	==	CNT_MAX-1)	//1s翻转led		
led_0	<=	~led_0;			
else					
led_0	<=	led_0;			
end					
else					
led_0	<=	1'd0;			
end					
					
//led1翻转					
always@(posedge	sys_clk)	begin			
if(!rst_n)					
led_1	<=	1'd0;			
else	if(key1_flag)				
led_1	<=	~led_1;			
end					
					
//----------------------------------instance----------------------------------------------					
//按键消抖模块					
btn_deb_fix#(					
BTN_WIDTH	(	4'd2	)	//2个按键	
)u_btn_deb_fix(					
sys_clk	(	sys_clk	"),"		
rst_n	(	rst_n	"),"		
btn_in	(	"{key1,key0}"	"),"		
btn_deb_fix	(	"{key1_flag,key0_flag}"	)		
);					
					
					
endmodule

CNT_MAX定义了一个最大的计数值,由于我们的系统时钟是25MHZ,也就是25000000,所以要让LED实现1s闪烁的话就是从0计数到24999999的时候让led进行一次翻转。

在26-31行中,利用系统时钟计数100个周期后产生了一个复位信号用来给后续模块和时序逻辑提供复位。

在43-55行中,当led0_en拉高时才开始计数一秒。否则计数器一直保持0.

在82-89行中,例化了一个按键消抖的模块,按键按下并松开后将产生一个脉冲信号即key0_flag,key1_flag,其中key_0flag控制led0闪烁,key1_flag控制led1翻转。

//按键消抖					
`define	UD	#1			
module	btn_deb_fix#(				
parameter	BTN_WIDTH	=	4'd8	//按键数量	
)					
(					
input	sys_clk	","			
input	wire	rst_n	","		
input	[BTN_WIDTH-1:0]	btn_in	","		
					
output	reg	[BTN_WIDTH-1:0]	btn_deb_fix		
);					
					
//----------------------------------parameter----------------------------------------					
parameter	CNT_20MS_MAX	=	32'd500_000	;	//20MS计数
					
//----------------------------------reg----------------------------------------------					
reg	[23:0]	cnt[BTN_WIDTH-1:0];	//计数器		
reg	[BTN_WIDTH-1:0]	btn_in_reg	;	//寄存按键信号	
//打一拍					
always	@(posedge	sys_clk)	begin		
btn_in_reg	<=	btn_in;			
end					
//----------------------------------消抖主要逻辑----------------------------------------------					
genvar	i;				
generate					
begin					
for(i=0;i<BTN_WIDTH;i=i+1)					
begin					
always	@(posedge	sys_clk)	begin		
if(!rst_n)					
cnt[i]	<=	24'd0;			
if	(btn_in_reg[i]	==	1'b0)	//按下时	计数20ms时归零
cnt[i]	<=	24'd0;			
else	if(cnt[i]==CNT_20MS_MAX)	//抖动区间有效时计数			
cnt[i]	<=	cnt[i];			
else					
cnt[i]	<=	cnt[i]	+	1'b1;	
end					
					
always	@(posedge	sys_clk)	begin		
if(!rst_n)					
btn_deb_fix[i]	<=	1'd0;			
else	if(cnt[i]==CNT_20MS_MAX-1)	//消抖后输出一个clk的高电平			
btn_deb_fix[i]	<=	1'b1;			
else					
btn_deb_fix[i]	<=	1'b0;			
end					
end					
end					
endgenerate					
					
endmodule

该部分为按键消抖模块,parameter定义了按键输入的数量,模块的输出将产生一个脉冲信号即产生一个持续一个clk的高电平信号。

在30-50行中,cnt会不断进行20ms的计数,当按键按下时,cnt归0,从0开始计数直到20ms。当计数到20ms的时候,就输出一个clk的高电平,即将btn_deb_fix置1,并让其只保持一个clk。

2.6.实验步骤

这里将会详细介绍从新建工程到下载程序的具体步骤,后续的工程将不再详细解释。

2.6.1.打开PDS软件,创建工程

Step1:打开PDS软件,点击NEW Project,然后对其设置完成新建工程。

rootfile1

Step2:单击NEXT

rootfile1

Step3:创建名为 led_water 的工程到对应的文件目录,之后单击 Next。

新建工程大致包括设置工程名和工程路径、工程类型、工程文件及器件信息。

【Project Name】是工程文件名称,默认为 project。(只允许字母、数字、下划线(_)、杠(-)、点(.))。

【Project Location】用于选择新工程的工作路径,文件夹名只允许字母、数字、下划线(_)、杠(-)、点(.)、@、~、,、+、=、#、空格( ),但空格不能出现在路径名首尾,即工程文件放置的路径。

【Create Preject Subdirectory】将工程文件名作为工作目录的一部分。

rootfile1

Step4:选择RTL project,点击Next。

【RTL Project】用于创建 RTL 工程。新建的工程可以执行 synthesize,device map,place& route,report timing, report power, generate netlist 及 generate bitstream 等。

【Post-Synthesize Project】用于创建综合后工程。新建的工程可以执行 device map,place& route,report timing,report power, generate netlist 及 generate bitstream 等。

rootfile1

Step5:单击Next

该界面可以 Add Files 和 Add Directories 来添加 rtl 源文件及新建 rtl 源文件,以及调整 rtl 文件编译顺序,Add Files 添加选中的文件,Add Directories 添加选中的文件夹下所有合适的文件,若勾选了下方的 Add source from subdirecotires 则添加所有的子目录下合适的文件,也可直接 NEXT 跳过添加文件。

rootfile1

Step6:单击Next

rootfile1

Step7:单击Next

rootfile1

Step8:选择器件系列、型号、封装、速率、综合工具,之后单击Next

synthesize tool 中可以选择综合工具为 Synplify Pro 或ADS,在实验中使用ADS综合工具。

rootfile1

Step9:在summary单击Finish,完成工程的创建

rootfile1

2.6.2 添加设计文件

PDS 软件界面如下图:

rootfile1

双击 Designs,将前面设计的 module 新建到文件中,或者将前面编辑好的verilog 文件添加到工程中:

rootfile1

添加文件到工程:

在窗口中点击 Add Files,选择添加文件到工程;

rootfile1

新建文件到工程:

1)在窗口中点击 Create File;

rootfile1

2)选择 Verilog Design File,文件名和 module 名一致,默认路径,点击 OK;

rootfile1

3)点击 OK;

rootfile1

4)点击 Cancel;

rootfile1

5)默认打开新建文件,将前面设计的 代码 复制进去,

rootfile1

6)点击保存,新建文件完成

rootfile1

Crtl+s保存。

rootfile1

双击Designs。

rootfile1

点击Add Files;

rootfile1

添加btn_deb_fix.v模块,即按键消抖模块。

rootfile1

点击OK。

2.6.3 编译

可采用以下方式运行 Compile 流程:

(1)双击 Flow 中的 Compile 进行综合;

(2)右击 Compile 点击 Run 进行综合;

rootfile1

2.6.4 工程约束

点击 Tools 选择 User Constraint Editor(Timing and Logic)或者点击工具栏图标 ,User Constraint Editor(Timing and Logic) 选择 Pre Synthesize UCE,如下图所示。

rootfile1

Tools 下的 User Constraint Editor(Timing and Logic)

rootfile1

工具栏 User Constraint Editor(Timing and Logic)图标

2.6.4.1 时钟约束

打开 UCE 后,选择 Timing Constraints 后选择 Create Clock 添加基准时钟,基准时钟一般是通过输入 port 输入用户所使用的板上时钟。

rootfile1

在弹窗中对时钟命名,关联时钟管脚,添加时钟参数,点击 OK 会创建一条时钟约束,Reset 重置该页面。创建完成如下图所示:

rootfile1

提供给开发板的时钟是25MHZ,即周期为40ns。

rootfile1
2.6.4.2 物理约束

打开 UCE 后,选择 Device 后选择 I/O,根据原理图编辑 IO 的分配。

rootfile1

按照原理图编辑好 IO 分配后,点击保存,会生成.fdc 文件,完成约束。

2.6.5 综合

运行 Synthesize 流程有以下四种方式可以实现:

(1)双击 Flow 中的 Synthesize 进行综合;

(2)右击 Synthesize 点击 Run 进行综合;

完成 Synthesize 操作后,会看到下图所示:

rootfile1

2.6.6 Device Map

Device Map 的主要作用是将设计映射到具体型号的子单元上(LUT、FF、Carry 等)。运行 Device Map 流程有以下方式可以实现:

(1)直接双击 Device Map;

(2)右击 Device Map 点击 Run;

完成 Device Map 操作后,会看到下图所示:

rootfile1

2.6.7 Place & Route

布局布线(Place & Route)根据用户约束和物理约束,对设计模块进行实际的布局及布线。运行 Place & Route 流程有以下方式可以实现:

(1)直接双击 Place & Route;

(2)右击 Place & Route 点击 Run;

完成 Device Map 操作后,会看到下图所示:

rootfile1

2.6.8 Generate Bitstream

Generate Bitstream 生成二进制位流文件。运行 Generate Bitstream 流程有以下方式可以实现:

(1)直接双击 Generate Bitstream;

(2)右击 Generate Bitstream 点击 Run;

完成以上操作,将会产生位流文件。运行 Generate Bitstream,可以看到界面如下图所示:

rootfile1

2.6.9 下载生成的位流文件

点击 Tools 选择 Configuration 或者点击工具栏图标 Configuration,如下图所示。

rootfile1

Tools 下的 Configuration

rootfile1

工具栏 Configuration 图标

打开 Configuration 后直接选择 Scan Device 直接进行扫描 Jtag 链操作,初始化链成功,会将链上扫描到的所有器件显示于工作区内,并在器件属性窗口显示当前器件的器件信息,并弹出对话显示能够为器件添加的配置文件:

rootfile1

初始化链成功

在对话框中选择位流文件,添加该配置文件,提示所载入文件的绝对路径并在信息栏中显示,如下图所示:

rootfile1

下载位流文件

rootfile1

当发现这4个信号均为1时,表示下载成功。

同时,开发板也配置了一个外部flash,其中,若需要将程序固化到板卡上需要将尾流文件转化为.sfc文件。

首先点击Configuration页面的Operations选项的Covert File选项。

rootfile1

点击后会出现如下画面,在Generate Flash Programing File页面选择对应的Flash 器件的厂商名、型号、再在BitStreamFile位置选择位流文件的路径,点击OK。(若使用的flash器件不在可选的flash列表中,需手动添加对应flash型号,操作步骤请参考开发板下载与固化相说明)

rootfile1

转化.sfc文件成功后,页面会如下图所示,点击OK。

rootfile1

用户可通过右键下图位置,点击Scan Outer Flash。

rootfile1

页面会显示板卡搭载的Flash的型号,点击.sfc文件,点击OPEN。

rootfile1

在下图位置点击鼠标右键后,点击Program。

rootfile1

固化Flash成功如下图所示:

rootfile1

此时将板卡断电,再重新上电,如果按下key0和key1能看到对应的实验现象的话,则表示固化成功。(等大概15s)

最后上板结果如下所示

3 Pango的时钟资源——锁相环

3.1 实验简介

实验目的: 了解PLL IP的基本使用方法。

实验环境: Window11 PDS2022.2-SP6.4

芯片型号: PG2L50H-484

3.2 实验原理

3.2.1 PLL介绍

锁相环作为一种反馈控制电路,其特点是利用外部输入的参考信号来控制环路内部震荡信号的频率和相位。因为锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。锁相环在工作的过程中,当输出信号的频率与输入信号的频率相等时,输出电压与输入电压保持固定的相位差值,即输出电压与输入电压的相位被锁住,这就是锁相环名称的由来。

锁相环拥有强大的性能,可以对输入到 FPGA 的时钟信号进行任意分频、倍频、相位调整、占空比调整,从而输出一个期望时钟;除此之外,在一些复杂的工程中,哪怕我们不需要修改任何时钟参数,也常常会使用 PLL 来优化时钟抖动,以此得到一个更为稳定的时钟信号。正是因为 PLL 的这些性能都是我们在实际设计中所需要的,并且是通过编写代码无法实现的,所以PLL IP核才会成为程序设计中最常用IP核之一。

PLL IP是紫光同创基于PLL及时钟网络资源设计的IP,通过不同的参数配置,可实现时钟信号的调频、调相、同步、频率综合等功能。

3.2.2 IP配置

首先点击快捷工具栏的“IP”图标,进入IP例化设置

rootfile1

然后在IP目录处选择PLL,在Instance name处为本次实例化的IP取一个名字,接着点击Customise进入IP配置页面。操作示意图如下:

rootfile1

PLL的使用可选择Basic和Advanced两种模式,Advanced模式下PLL的内部参数配置完全开放,需要自己填写输入分频系数、输出分频系数、占空比、相位、反馈分频系数等才能正确配置。Basic模式下用户无需关心PLL的内部参数配置,只需输入期望的频 率值、相位值、占空比等,IP将自动计算,得到最佳的配置参数。如果没有特殊应用,建议使用Basic模式配置PLL。本次实验我们选择Basic Configuration。

rootfile1

接下来进行基础配置:

在Public Configurations一栏将输入时钟频率设置为25MHZ。

在Clockout0 Configurations选项卡下,勾选使能clkout0,将输出频率设置为50MHZ。

在Clockout1 Configurations选项卡下,勾选使能clkout1,将输出频率设置为100MHZ。

在Clockout2 Configurations选项卡下,勾选使能clkout2,将输出频率设置为100MHZ,并设置相位偏移为180度。

其他选项可以使用默认设置,若有其他需求可以查阅IP手册了解,本实验我们暂介绍IP基本的使用方法:

rootfile1

点击左上角generate生成IP。

rootfile1

3.3 代码设计

模块接口列表如下所示:

PLL IP使用实验模块接口表

端口I/O位宽描述
sys_clkinput1系统时钟
clkout0output154MHZ时钟
clkout1output181MHZ时钟
clkout2output181MHZ时钟,相位偏移180度
lockoutput1时钟锁定信号,当为高电平时,代表IP核输出时钟稳定。

PLL_TEST顶层代码:

module PLL_TEST(  
    input                               sys_clk                    ,  
    output                              clkout0                    ,  
    output                              clkout1                    ,  
    output                              clkout2                    ,  
    output                              lock                          
   );  
  
PLL PLL_U0 (  
    .clkout0                            (clkout0                   ),// output  
    .clkout1                            (clkout1                   ),// output  
    .clkout2                            (clkout2                   ),// output  
    .lock                               (lock                      ),// output  
    .clkin1                             (sys_clk                   ) // input  
);  
  
 
endmodule

该模块的功能是例化PLL IP核,功能简单,在此不做说明。

PLL_tb测试代码:

timescale 1ns / 1ps  
 
module    PLL_tb();  
    reg                                        sys_clk                    ;  
    wire                                       clkout0                    ;  
    wire                                       clkout1                    ;  
    wire                                       clkout2                    ;  
    wire                                       lock                       ;  
  
  
  
    initial  
        begin  
            #2                                               
                    sys_clk <= 0     ;                            
        end                                                  
                                                             
    parameter   CLK_FREQ = 25;//Mhz                         
    always # ( 1000/CLK_FREQ/2 ) sys_clk = ~sys_clk ;                
                                                             
                                                             
PLL_TEST u_PLL_TEST(  
    .sys_clk                            (sys_clk                   ),  
    .clkout0                            (clkout0                   ),  
    .clkout1                            (clkout1                   ),  
    .clkout2                            (clkout2                   ),  
    .lock                               (lock                      )  
);  
 
  
endmodule

timescale定义了模块仿真的时间单位和时间精度。时间单位是1纳秒,精度是1皮秒。

initial块负责初始化系统时钟。在仿真启动后的2纳秒,系统时钟sys_clk被设置为0。这是为了在仿真开始时定义一个已知的初始状态。

代码定义了一个时钟频率参数CLK_FREQ为25 MHz,并使用一个always块来翻转系统时钟信号。always块中的逻辑使得sys_clk每40纳秒翻转一次,从而生成一个25 MHz的方波时钟信号。这种时钟信号用于驱动被测试的PLL_TEST模块。

最后,将测试平台的各个信号连接到PLL_TEST模块。这包括将生成的系统时钟sys_clk连接到PLL_TEST的时钟输入端,并将PLL_TEST的输出信号clkout0、clkout1、clkout2和lock使用wire引出观察。

3.4 PDS与Modelsim联合仿真

PDS支持与Modelsim或QuestaSim等第三方仿真器的联合仿真,而Modelsim是较为常用的仿真器,使用PDS与Modelsim来进行联合仿真。

接下来选择Project->Project Setting,打开工程设置,准备设置联合仿真。

rootfile1

选择Simulation选项卡,红框1选择刚才编译生成的仿真库的路径,红框2选择Modelsim的启动路径,之后点击OK。

右键仿真的文件,选择Run Behavior Simulation开始行为仿真。

rootfile1

运行后会自动打开Modelsim。并执行仿真,如果没有任何报错,则表示成功。如果出现错误,请检测PDS与Modelsim的配置。

rootfile1

3.5 实验现象

点击Wave观察PLL输出信号:

rootfile1

使用标尺测量clkout0,发现其一个时钟周期是20ns,也就是50MHZ。

rootfile1

可以看到clkout1的时钟频率是100MHZ,且和clkout2相位偏差180°,符合设置。需要注意PLL的输出时钟应该在时钟锁定信号lock有效之后才能使用,lock信号拉高之前输出的时钟是不确定的。

4 ROM、RAM、FIFO的使用

4.1 实验简介

实验目的: 掌握紫光平台的RAM、ROM、FIFO IP的使用

实验环境: Window11 PDS2022.2-SP6.4

芯片型号: PG2L50H-484

4.2 实验原理

不管是Logos系列或者是Logos2系列,其IP配置以及模式和功能均一致,不会像PLL那样有动态配置以及内部反馈选项的选择等之间的差异,所以是RAM、ROM、FIFO是通用的。

4.2.1 RAM介绍

RAM即随机存取存储器。它可以在运行过程中把数据写进任意地址,也可以把数据从任意地址中读出。其作用可以拿来做数据缓存,也可以跨时钟,也可以存放算法中间的运算结果等。

注意,PDS的IP配置工具中提供两种不同的RAM,一种是Distributed RAM(分布式RAM)另一种是DRM Based RAM,分布式RAM用的是LUT(查找表)资源去构成的RAM,这种RAM会消耗大量LUT资源,因此通常在一些比较小的存储才会用到这种RAM,以节省DRM资源。而DRM Based RAM是利用片内的DRM资源去构成的RAM,不占用逻辑资源,而且速度快,通常设计中均使用DRM Based RAM。

RAM分为三种,如下表所示:

RAM类型特点
单端口RAM只有一个端口可以读写。只有一个读写口和地址口
伪双端口RAM有wr和rd两个端口,顾名思义,wr只能写,rd只能读
真双端口RAM提供A和B两个端口,两个端口均可以独立进行读写

注意,当使用真双端口时,要避免出现同时读写同个地址,这会造成写入失败,在逻辑设计上需要避开这个情况。

以下给出比较常用的RAM的配置作为介绍,通常我们比较常用伪双端口RAM来设计,如下图所示:

rootfile1

下图为IP配置:

rootfile1

注意,如果勾选Enable Output Register(输出寄存),输出数据会延迟一个时钟周期。

具体每个端口的含义这里参考官方手册,大家也可以自行查看IP手册,如下图所示:

rootfile1

DRM Resource Type:用于配置所建 RAM IP核用的是哪种资源,不同芯片型号可选资源是不一样的,有的是9K,有的是18K,有的是36K,如果没有特殊情况,直接AUTO即可。

4.2.1.1 RAM的读写时序

配置成不同模式的时候,RAM的读写时序是不一样的,真双端口和单端口的RAM配置均有三种模式,而伪双端口只有一种。由于真双端口和单端口的配置是一样的,这里以真双端口为例子。

分为NORMAL_WRITE(正常模式)、TRANSPARENT_WRITE(直写)、READ_BEFORE_WRITE(读优先模式)三种模式。

rootfile1

而伪双端口不属于上面三种模式,有它独特的模式。这几种模式的差异就在于读写时序的不同,接下来,我们来分析读写时序。

以下时序图均来自官方IP手册,并且均未使能输出寄存。注意wr_en为1时表示写数据,为0表示读数据。

(1)NORMAL_WRITE

rootfile1

在NORMAL_WRITE这种模式下,可以看到,当时钟的上升沿到来,且clk_en和wr_en均为高电平时,就会把数据写到对应的地址里面,如图中的1时刻。然后看读数据端口,当wr_en不为0的时候,a_rd_data一直为Don’t Care状态,而当时钟上升沿到来,且clk_en为高电平,wr_en为低电平时,a_rd_data输出当前a_addr里的数据,即Mem(ADDR1)和ADDR0里的D0。

(2)READ_BEFORE_WRITE

rootfile1

在READ_BEFORE_WRITE这种模式下,可以看到在1的时刻,时钟上升沿到来, 且clk_en和wr_en均为高电平,D0写进了ADDR0里面,但是注意看此时的a_rd_data和a_addr,可以发现,此时a_wr_en并不为0,可a_rd_data还是输出了上一刻ADDR0的数据(因为不是输出D0)。之后,a_wr_en拉低,此时才是读数据,在3时刻,把ADDR0的数据读出来,a_rd_data才输出了D0。

所以总结一下,这个模式其实就是进行写操作时,读端口会把当前写的地址的原始数据输出,因此叫读优先模式很合情合理对吧,顾名思义,就是优先把原来的数据读出来。

(3)Transparent_Write

rootfile1

在Transparent_Write这种模式下,可以看到在1的时刻,时钟上升沿到来, 且clk_en和wr_en均为高电平,D0写进了ADDR0里面,但是注意看此时的a_rd_data和a_addr,可以发现,此时a_wr_en并不为0,可a_rd_data居然直接输出了D0,之后a_wr_en拉低,进入读状态,在2时刻,再一次把ADDR0的数据读出来,输出了D0。

分析总结一下,根据1时刻的情况,我们可以得出结论,在这种模式下,当我们进行写操作时,读端口会马上输出我们写入的数据。所以叫直写模式。

(4)伪双端口的读写时序

注意:wr_en为1时是写操作,为0是读操作。

伪双端口的读写时序与上面三种都不同,我们看下图的时序来分析:

rootfile1

注意看1时刻,此时wr_en和wr_clk_en均为高电平,所以是写操作,所以1时刻就是往地址ADDR0里写入D0,注意此时的rd_addr和rd_data,可以看到这一时刻rd_addr是ADDR2,然后进行写操作时,rd_data同样输出了ADDR2里的数据,而此时wr_en还是高电平。接下来看2和3时刻,此时wr_en为0,rd_clk_en是高电平,所以是读操作,此时分别读出ADDR1和ADDR0里的数据,之后rd_clk_en变成低电平,读时钟无效,可以看到rd_data保持D0输出。

分析总结一下,主要是1时刻,大家可以看到1时刻往ADDR0写入了D0,读端口却输出了ADDR2中的数据。仔细观察可以得出结论:伪双端口RAM在进行写操作的时候,会把当前读端口指向的地址的数据输出。是不是有点像直写?只不过直写是输出写入的数据,而伪双端口是输出读端口指向的地址的数据。

2.1.1.2 ROM介绍

ROM即只读存储器,在程序的运行过程中他只能被读取,无法被写入,因此我们应该在初始化的时候就给他配置初值,一般是在生成IP的时候通过导入.dat文件对其进行初值配置。

注意,PDS的IP配置工具中提供两种不同的ROM,一种是Distributed ROM(分布式ROM)另一种是DRM Based ROM,分布式ROM用的是LUT(查找表)资源去构成的ROM,这种ROM会消耗大量LUT资源,因此通常在一些比较小的存储才会用到这种RAM,以节省DRM资源。而DRM Based ROM是利用片内的DRM资源去构成的ROM,不占用逻辑资源,而且速度快,通常设计中均使用DRM Based ROM。

以下给出比较常用的ROM的配置作为介绍,由于只能读,因此其均为单端口ROM,如下图所示:

rootfile1

下图为IP配置:

rootfile1

注意,如果勾选Enable Output Register(输出寄存),输出数据会延迟一个时钟周期。

同时,可以看到Enable Init选项是默认勾选的,并且不可取消。

导入的数据的格式只能为二进制或者是十六进制,demo选择十六进制。

具体每个端口的含义这里参考官方手册,大家也可以自行查看IP手册,如下图所示:

rootfile1

一般我们只需要addr、rd_data、clk、rst这四个信号即可。

以下时序图均来自官方IP手册,并且均未使能输出寄存。

4.2.1.2 ROM的读时序
rootfile1

可以看到该时序是非常简单的,比如在TI时刻,当clk上升沿到来时,且clk_en为高电平时,给出要读出的地址,rd_data就会输出数据,在不勾选输出使能寄存的情况下,rd_data的输出会有延迟,具体时间可以从仿真里看到,所以我们在下个时钟周期的上升沿即T2时刻的上升沿才能获取到ROM读出的值。

所以整体时序非常简单,如果勾选了clk_en信号,就要给clke_en高电平才能读数据,如果不勾选clk_en信号,就一直根据地址读取ROM数据。

4.2.2 FIFO介绍

FIFO即先入先出,在FPGA中,FIFO的作用就是对存储进来的数据具有一个先入先出特性的一个缓存器,经常用作数据缓存或者进行数据跨时钟域传输。FIFO和RAM最大的区别就是FIFO不需要地址,采用的是顺序写入,顺序读出。

在紫光的IP工具中又分为Distribute FIFO和DRM FIFO,其实就是用不同的资源去构成,前者Distribute FIFO也就是分布式FIFO,使用的是片上的LUT资源去构成,而DRM FIFO使用的是片上的DRM资源去构成,DRM构成的FIFO其性能大于LUT资源构成的,不仅容量更大,且可配置更多功能。

本章着重介绍DRM Based FIFO。

注意:FIFO 写满后禁止继续写入数据,否则将会写溢出。

注意:FIFO 读空后禁止继续读数据,否则将会读溢出。

以下给出常用的FIFO的配置作为介绍。

rootfile1rootfile1

注意,如果勾选Enable Output Register(输出寄存),输出数据会延迟一个时钟周期。

FIFO Type有SYNC和ASYNC两种,第一种是同步FIFO,读写端口共用一个时钟和复位,另一种是异步FIFO,读写时钟和复位均独立。在平常设计中,比较常用的是异步FIFO,因为同步FIFO和异步FIFO的读写时序一模一样,只有读写端口的时钟复位有差异,当异步FIFO的读写端口使用相同的时钟和复位,此时异步FIFO和同步FIFO基本是一致的。

Reset Type也可以选择SYNC和ASYNC两种,SYNC模式下需要时钟的上升沿采样到复位有效才会复位,而在ASYNC模式下,复位一旦有,FIFO立即复位。

其余端口说明引用官方IP手册,如下图所示:

rootfile1

其中rd_water_level和wr_water_level分别代表”可读的数据量”和”已写入的数据量”,其含义与Xilinx的FIFO的wr_data_count和rd_data_count是一致的。

rootfile1

当我们将Enable Almost Full Water Level和Enable Almost Empty Water Level勾选上,才能看到rd_water_level和wr_water_level,而下面的Almost Full Numbers的设置是表示当写入1020个数据时,Almost Full信号就会拉高,Almost Empty Numbers的设置表示当可读数据剩下4个时Almost Empty信号就会拉高。

同时FIFO支持混合位宽,例如写端口16bit,读端口8bit。如果写入16’h0102,那么读出来会是8’h02,8’h01,会先读出低位。

如果写端口8bit,读端口16bit。当写入8’h01,8’h02时,读出来是16’h0201,先写入的数据存放在低位。

4.2.2.1 FIFO的读写时序

因为同步FIFO和异步FIFO的读写时序一致,这里用异步FIFO的读写时序图来做介绍。

注意:复位时高电平有效。读出数据均未勾选Enable Output Register(输出寄存)。

(1)FIFO未满时的写时序

rootfile1

可以看到在1时刻,复位信号时低电平,处于工作状态,此时在wr_clk的上升沿且wr_en为高电平时将数据D0写入FIFO,wr_water_level也从0变1,表示已经写入了一个数据,此时注意看读端口的empty信号,在3时刻empty信号从高变低,意味着读端口已经有数据可以读了,FIFO不再为空,而注意看,rd_clk和wr_clk是不一样的,从1写入到3时刻empty拉低时,经过了3个rd_clk。

所以这里我们可以得出结论:rd_water_level要滞后wr_water_level三个rd_clk。

(2)FIFO将满时的写时序

rootfile1

将满时主要分析full和almost_full信号。假设Almost Full Numbers设置为N-2,在1时刻,此时已经写入了N-6个数据,意味着再写6个数据FIFO就满了,从1时刻到2时刻一共写入了4个数据,因此当wr_water_level变成N-2时,满足条件,可以看到Almost Full信号拉高,再写两个数据FIFO就满了,所以再经过两个时钟周期后,Full信号拉高。

(3)FIFO在满状态下的读时序

rootfile1

在满状态下,FIFO已经有N个数据了,此时在1状态下,rd_clk的上升沿,且rd_en为高电平时,此时从FIFO里读出数据(数据的输出有延时,仿真中延时0.2ns)。此时rd_water_level变成N-1,rd_data输出D0。然后看2时刻,full信号拉低,此时可以看以下,在1时刻到2时刻期间一共经过了3个wr_clk写端口才能判断到此时数据量已经不为满。 所以我们可以得出结论,wr_water_level要滞后rd_water_level三个wr_clk。

(4)FIFO将空时的读时序

rootfile1

在1时刻,可读的数据量剩下4,假设Almost Empty Number设为2,在1时刻和2时刻分别读出了两个数据,所以在2时刻下,可读数据量剩下两个,达到Almost Empty Number触发条件,因此almost_empty信号拉高,再过两个时钟周期,即再读两个数据,FIFO将变成空状态,也就是状态3,此时empty信号拉高。

2.1.2接口列表

该部分介绍每个顶层模块的接口。

ram_test_top.v

端口I/O位宽描述
wr_clkinput1写时钟
rd_clkinput1读时钟
rst_ninput1全局复位
rw_eninput11:写操作 0:读操作
wr_addrinput5写地址
rd_addrinput5读地址
Wr_datainput8写入RAM的数据
Rd_dataoutput8从RAM读出的数据

rom_test_top.v

端口I/O位宽描述
rd_clkinput1读时钟
rst_ninput1全局复位
rd_addrinput10读地址
rd_datainput64从ROM读出的数据

fifo_test_top.v

端口I/O位宽描述
sys_clkinput1写/读时钟
rst_ninput1全局复位
wr_addrinput8写入FIFO的数据
wr_eninput1写使能
rd_eninput1读使能
wr_water_leveloutput8已写入FIFO的数据量
rd_water_leveloutput8可从FIFO读出的数据量
Rd_dataoutput8从FIFO读出的数据

4.3 工程说明

暂无

4.4 代码仿真说明

本次的顶层模块实际就是例化IP,然后把端口引出而已,主要代码都在testbench里面,所以我们直接介绍仿真代码。

4.4.1 RAM仿真测试

`timescale	1ns/1ns						
module	ram	test	tb()				
reg	sys	clk					
reg	rd	clk					
reg	rst	n					
reg	rw	en	//读写使能信号				
							
reg	[7:0]	wr	data				
reg	[4:0]	wr	addr				
reg	[4:0]	rd	addr				
							
wire	[7:0]	rd	data				
							
reg	[1:0]	state					
							
initial							
begin							
rst	n	<=	1'd0				
sys	clk	<=	1'd0				
rd	clk	<=	1'd0				
#20							
rst	n	<=	1'd1				
							
end							
							
//读写控制							
always@(posedge	sys	clk	or	negedge	rst	n)	begin
if(!rst	n)						
begin							
state	<=	2'd0					
wr	data	<=	8'd0				
rw	en	<=	1'd0				
wr	addr	<=	8'd0				
rd	addr	<=	8'd0				
end							
else							
begin							
case(state)							
2'd0:begin							
rw	en	<=	1'd1				
state	<=	2'd1					
end							
							
2'd1:begin							
if(wr	addr	==	5'd31)				
begin							
rw	en	<=	1'd0				
state	<=	2'd2					
wr	data	<=	8'd0				
wr	addr	<=	5'd0				
rd	addr	<=	5'd0				
end							
else							
begin							
state	<=	2'd1					
wr	data	<=	wr	data+1'b1			
rd	addr	<=	rd	addr+1'b1			
wr	addr	<=	wr	addr+1'b1			
end							
end							
2'd2:begin							
if(rd	addr	==	5'd31)				
begin							
state	<=	2'd3					
rd	addr	<=	5'd0				
end							
else							
begin							
state	<=	2'd2					
rd	addr	<=	rd	addr+1'b1			
end							
end							
2'd3:begin							
state	<=	2'd0					
end							
							
default:	state	<=	2'd0				
endcase							
end							
end							
							
//50MHZ							
always#10	sys	clk	=	~sys	clk		
							
//							
GTP	GRS	GRS	INST(				
GRS	N(1'b1)						
)							
							
ram	test	top	u	ram	test	top(	
wr	clk	(	sys	clk	"),"		
rd	clk	(	sys	clk	"),"		
rst	n	(	rst	n	"),"		
rw	en	(	rw	en	"),"		
wr	addr	(	wr	addr	"),"		
rd	addr	(	rd	addr	"),"		
wr	data	(	wr	data	"),"		
rd	data	(	rd	data	)		
)							
endmodule

涉及到tb的一些基础操作这里就不再详细讲解,只关注重点逻辑部分。从代码的27行到80行是ram的读写控制状态机。主要用来控制读写地址的生成和使能以及写入的数据。这里只讲解主要实现的功能,首先代码的38-42行,也就是state=0的时候,拉高rw_en,并跳转到状态1,此时进入写操作(没有使能clk_en,可以不管),下个时钟周期开始写入数据(注意是时序逻辑,边沿采样,所以是下个时钟周期才开始写数据),即state=1的时候是一直在往ram里面写数据,在代码的44到60行就是写操作了,可以看到,当wr_addr不等于31的时候,wr_data和wr_addr不断加1(rd_addr这里+1,可以看视频讲解,主要为了验证伪双端口的时序),当wr_addr等于31的时候,在下个时钟周期把数据清0,状态跳转,在当前时钟周期下还会再往地址31里面写入数据,所以在该时钟周期,一共写入了32个数据(从地址0写到地址31)。即状态1完成写入32个数据后跳转到state=2的逻辑。代码的61-72行,也就是state=2的时候,在每个周期的上升沿让rd_addr不断累加,直到rd_addr=31的时候,在下个时钟周期清空地址并让状态跳转的操作,而在当前时钟周期会继续把地址31的数据读出来,完成读取地址0-31的数据,一共32个数据,所以该状态主要完成读取32个数据,然后在下个时钟周期就跳转到state=3。state=3可以看到其主要作用就是等待一个时钟周期,然后跳转回去state=0下,起到一个延时作用。

rootfile1

上图为写数据的波形,数据从0开始递增到31,地址也是从0到31。

rootfile1

上图为读数据波形,从地址0-31读出了0-31个数据。

具体波形大家可以看视频仿真,或者自己尝试仿真,根据波形来看代码。因为这里是时序逻辑,所以如果是初学者,纯看文字可能会对rd_addr=31这一时刻还会再读一个数据感到疑惑,建议直接仿真,或者观看视频讲解的仿真部分,可以帮助快速理解。

可以总结出一句话就是时序逻辑的赋值总在下一个时钟周期才生效。所以在rd_addr=31时执行的操作要在下一个时钟周期才会被采样生效。所以当前时钟还是会再从RAM读出一个数据。

4.4.2 ROM仿真测试

`timescale 1ns/1ns  
module rom_test_tb();  
reg    sys_clk;  
reg    rst_n;  
reg    [9:0]    rd_addr;  
wire   [63:0]    rd_data;  
  
initial  
begin  
    rst_n    <=    1'd0;  
    sys_clk  <=    1'd0;  
    #20  
    rst_n    <=    1'd1;  
  
end  
 
//50MHZ  
always#10 sys_clk = ~sys_clk;  
//  
GTP_GRS GRS_INST(  
    .GRS_N(1'b1)  
    ) ;  
  
always@(posedge sys_clk or negedge rst_n)    begin  
    if(!rst_n)  
        rd_addr    <=    10'd0;  
    else      
        rd_addr    <=    #2 rd_addr + 1'b1;  
end  
  
rom_test_top u_rom_test_top(  
    .rd_clk   ( sys_clk  ),  
    .rst_n    ( rst_n    ),  
    .rd_addr  ( rd_addr  ),  
    .rd_data  ( rd_data  )  
);  
  
endmodule

代码31-36行例化了ROM的顶层模块,该模块里面其实就是调用了ROM IP,然后把信号引出端口,没有任何逻辑操作。

代码24-29行通过一个always块不断生成地址,任何给到ROM IP,将数据读出,由于没勾选clk_en信号,所以数据在ROM复位完成后就会不断读出。所以并没有复杂的逻辑,就是让地址从0不断累加,把数据读出。

rootfile1

上图为读出数据的波形图,可以看到读出的数据和dat文件里的数据一致。

rootfile1

4.4.3 FIFO仿真测试

`timescale	1ns/1ns				
module	fifo_test_tb();				
					
reg	sys_clk;				
reg	rst_n;				
					
reg	[7:0]	wr_data;			
reg	wr_en;				
reg	rd_en;				
					
reg	rd_state;	//读状态			
reg	wr_state;				
					
wire	[7:0]	rd_data;			
reg	[7:0]	rd_cnt;			
					
wire	[7:0]	rd_water_level;			
wire	[7:0]	wr_water_level;			
					
initial					
begin					
rst_n	<=	1'd0;			
sys_clk	<=	1'd0;			
#20					
rst_n	<=	1'd1;			
					
					
end					
					
always#10	sys_clk	=	~sys_clk;	//50MHZ	
					
always@(posedge	sys_clk	or	negedge	rst_n)	begin
if(!rst_n)					
begin					
wr_state	<=	1'd0;			
wr_en	<=	1'd0;			
wr_data	<=	8'd0;			
end					
else					
begin					
case(wr_state)					
1'd0:	if(wr_water_level	==	127)	//128个数据	
begin					
wr_en	<=	#2	1'd0;		
wr_data	<=	#2	8'd0;		
wr_state	<=	#2	1'd1;		
end					
else					
begin					
wr_en	<=	#2	1'd1;		
wr_data	<=	#2	wr_data+1'b1;		
wr_state	<=	#2	1'd0;		
end					
					
1'd1:	if(rd_cnt	==	127)		
wr_state	<=	#2	1'd0;		
					
					
default:	wr_state	<=1'd0;			
endcase					
end					
end					
					
always@(posedge	sys_clk	or	negedge	rst_n)	begin
if(!rst_n)					
begin					
rd_state<=	1'd0;				
rd_en	<=	1'd0;			
rd_cnt	<=	8'd0;			
end					
else					
begin					
case(rd_state)					
1'd0:	if(rd_water_level	>=	8'd128)	//等待128个数据	
begin					
rd_state	<=	#2	1'd1;		
rd_en	<=	#2	1'd1;		
end					
else					
begin					
rd_cnt	<=	#2	8'd0;		
rd_state	<=	#2	1'd0;		
end					
					
1'd1:	begin				
					
rd_cnt	<=	#2	rd_cnt	+	1'b1;
if(rd_cnt	==	127)			
begin					
rd_en	<=	#2	1'd0;		
rd_state	<=	#2	1'd0;		
end					
end					
default:	rd_state	<=	1'd0;		
endcase					
end					
end					
					
GTP_GRS	GRS_INST(				
GRS_N(1'b1)					
)	;				
					
fifo_test_top	u_fifo_test_top(				
sys_clk	(	sys_clk	"),"		
rst_n	(	rst_n	"),"		
wr_data	(	wr_data	"),"		
wr_en	(	wr_en	"),"		
rd_en	(	rd_en	"),"		
wr_water_level	(	wr_water_level	"),"		
rd_water_level	(	rd_water_level	"),"		
rd_data	(	rd_data	)		
);					
endmodule

涉及到tb的一些基础操作这里就不再详细讲解,只关注重点逻辑部分。整个设计分为读写两个状态的控制。分别完成了写入128个数据,和读出128个数据,由于FIFO不需要地址,所以只需要产生使能信号即可。

首先看写状态,在wr_state=0时,拉高写使能,并让wr_data不断累加,往FIFO里面写数据,当wr_water_level=127的时候,拉低写使能,写数据置0,写状态跳转到1,注意此时还会再写入一个数据,所以到此一个写入了128个数据。至于拉低写使能,写数据置0,写状态跳转到1这些操作将在下一个时钟周期才会被采样生效。之后,在wr_state=1时,不断等待rd_cnt,该条件就是判断当读出128个数据的时候,wr_state跳转到0状态。

接下来看读状态,在rd_state=0的时候,一旦可读的数据量超过128个(包括128),状态跳转到rd_state=1下,然后开始读出数据,同时在rd_state=1下用变量rd_cnt对我们的读出数据也进行计数,rd_cnt从0开始计数,当rd_cnt=127的时候会再往FIFO读出一个数据,所以此时就一共读出了128个数据,下一个时钟周期rd_en和rd_state都将置0。

rootfile1

写数据的波形如上所示,一共写入128个数据,从1写到128。

rootfile1

读数据的波形如上所示,一共读出128个数据,从1读到128。

具体波形大家可以看视频仿真,或者自己尝试仿真,根据波形来看代码。因为这里是时序逻辑,所以如果是初学者,纯看文字可能会对rd_cnt=127这一时刻还会再读一个数据感到疑惑,建议直接仿真,或者观看视频讲解的仿真部分,可以帮助快速理解。

可以总结出一句话就是时序逻辑的赋值总在下一个时钟周期才生效。所以在rd_cnt=127时执行的操作要在下一个时钟周期才会被采样生效。所以当前时钟rd_en还是为1,会再从FIFO读出一个数据。

5 DDR3读写实验例程

5.1 实验简介

实验目的: 完成DDR3的读写测试。

实验环境: Window11 PDS2022.2-SP6.4

芯片型号: PG2L50H-484

5.2 实验原理

开发板集成1颗4Gbit(512MB)DDR3 芯片,型号为MT41K256M16。DDR3 的总线宽度共为16bit。DDR3 SDRAM 的最高数据速率1066Mbps。

5.2.1 DDR3控制器简介

PG2L50H为用户提供一套完整的DDR memory 控制器解决方案,配置方式比较灵活,采用软核实现DDR memory的控制,有如下特点:

  • 支持DDR3
  • 支持x8、x16 Memory Device
  • 最大位宽支持32 bit
  • 支持精简的AXI4 总线协议
  • 一个AXI4 256 bit Host Port
  • 支持Self_refresh,Power down
  • 支持Bypass DDRC
  • 支持DDR3 Write Leveling 和DQS Gate Training
  • DDR3 最快速率达1066 Mbps

5.3 工程说明

PDS安装后,需手动添加DDR3 IP,请按以下步骤完成:

DDR3 IP文件:PG2L_IP\PG2L_IP\DDR3\ips2l_hmic_s_v1_10.iar

rootfile1

5.3.1 DDR3读写Example工程

打开PDS软件,新建工程ddr3_test,点开如下图标,打开IP Compiler;

rootfile1

选择DDR3 IP,取名ddr3_test,然后点击Customize;

rootfile1

在DDR3设置界面中Step1按照如下设置:

rootfile1

Step2按照如下设置,需要自己新建DDR3模型,选择MT41K256M16XX为模板,并保持Timing parameters和地址以及Drive Options和下图设置的一致。

rootfile1

Step3按照如下设置,勾选Custom Control/Address Group,管脚约束参考原理图:

rootfile1rootfile1

提醒:

在设置IP核时,step 3:pin/bank options 中,管脚设置中的Group Number 与原理图的对应关系如下图

rootfile1

R5表示BANK5,G1表示Group Nmuber为1。

rootfile1

Step4为概要,点击Generate可生成DDR3 IP;

rootfile1

关闭本工程,按此路径打开Example工程:

Xxxxx\ddr3_test\ip_core\ddr3\pnr

rootfile1

打开顶层文件,需要对顶层文件进行修改,具体参考详细代码。下图是经过修改后的顶层文件。

rootfile1

对“Step3已做管脚约束”外的其他管脚,对照原理图使用UCE工具进行修改,移植的话可以直接参考工程的fdc文件进行移植。

rootfile1

以下管脚可约束在LED,方便观察实验现象;

rootfile1

可按以下方式查看IP核的用户指南,了解Example模块组成;

rootfile1

5.4 实验现象

下载程序,可以看到LED1常亮,LED3闪烁, LED4闪烁,LED5常亮;

信号名称参考说明LED编号
err_flag_led数据检测错误信号3
heart_beat_led心跳信号4

提醒:

Heart_beat_led信号闪烁表示ddrphy系统时钟正常。

rootfile1

err_flag_led 信号闪烁说明数据检测无错误。可在IP核数据手册中找到。

rootfile1

如果正常的话,err_flag_led闪烁的速度快于heart_beat_led的闪烁速度。

6 光纤通信测试实验例程

6.1 实验简介

实验目的: 通过光纤连接实现光模块之间的数据收发。

实验环境: Window11 PDS2022.2-SP6.4

芯片型号: PG2L50H-484

6.2 实验原理

PG2L100H内置了线速率高达6.6Gbps 高速串行接口模块,即HSSTLP,包含1个HSSTLP,共4个全双工收发LANE,除了PMA,HSSTLP 还集成了丰富的PCS 功能,可灵活应用于各种串行协议标准。在产品内部,每个HSST 支持1~4 个全双工收发LANE。HSST 主要特性包括:

  • 支持 DataRate 速率:0.6Gbps-6.6Gbps
  • 灵活的参考时钟选择方式
  • 发送通道和接收通道数据率可独立配置
  • 可编程输出摆幅和去加重
  • 接收端自适应线性均衡器 Logos2系列FPGA器件数据手册
  • PMA Rx 支持 SSC
  • 数据通道支持数据位宽:8bit only, 10bit only, 8b10b, 16bit only, 20bit only, 32bit only, 40bit only,64b66b/64b67b 等模式
  • 可灵活配置的 PCS,可支持 PCI Express GEN1, PCI Express GEN2,XAUI,千兆以太 网,CPRI,SRIO 等协议
  • 灵活的 Word Alignment 功能
  • 支持 RxClock Slip 功能以保证固定的 Receive Latency
  • 支持协议标准 8b10b 编码解码
  • 支持协议标准 64b66b/64b67b 数据适配功能
  • 灵活的 CTC 方案
  • 支持 x2 和 x4 的 Channel Bonding
  • HSSTLP 的配置支持动态修改
  • 近端环回和远端环回模式
  • 内置 PRBS 功能
  • 自适应

6.3 工程说明

6.3.1 安装HSST IP核

PDS安装后,需手动添加HSST IP,请按以下步骤完成:

(1)HSST IP文件:选择1_9.iar

rootfile1

(2)IP安装步骤:请查看 “工具使用篇\03_IP核安装与查看用户指南”

rootfile1

6.3.2 光纤通信测试例程

打开PDS软件,新建工程hsst_test,点开如下图标,打开IP Compiler;

rootfile1

选择HSST IP,取名,然后点击Customize;

rootfile1

在HSST设置界面中Protocol and Rate按照如下设置,Channel0、 Channel1、Channel3为DISABLE,Channel2设置为Fullduplex(全双工),Protocol(协议)选择CUSTOMERIZEDX1(自定义模式),TX Line Rate和RX Line Rate均选择6.25Gbps,Encoder均选择8B10B,Data Width选择32bit,时钟选择Diff_REFCK0,选择125MHZ。

rootfile1

Alignment and CTC按照如下设置,Word Align Mode选择CUSTOMERIZED_MODE。控制字(COMMA code-group select)选择K28.5,CTC_MODE选择Bypassed。

rootfile1

Misc按照如下设置,时钟选择25MHZ,其余保持默认,然后点击Generate可生成HSST IP;

rootfile1

关闭本工程,按此路径打开Example工程: hsst_test\hsst_test\ipcore\hsst_test\pnr\example_design

rootfile1

为了能在开发板上运行,需对顶层文件hsst_test_dut_top的复位进行修改,详情请查看例程顶层文件:

rootfile1

上图是修改前的顶层文件。

下图是修改后的顶层文件。tx_disable需要拉低才能打开SFP发射功能。

rootfile1rootfile1

上图为部分管脚约束,具体需要查看工程的fdc文件,注意图中红色方框部分是hsst_lane和hsst_pll的位置约束,是必须要添加的,差分数据管脚可以不用约束,提供给hsst的参考时钟必须约束即(i_p_refckn_0和i_p_refckp_0)。

rootfile1

为了观察收发数据是否有误,需要进行Debugger插核操作。

rootfile1

时钟选择o_p_clk2core_tx_2。

可按以下方式查看IP核的用户指南,了解Example模块组成;

rootfile1

6.4 实验现象

注:例程位置:hsst_test\hsst_test\ipcore\hsst_test\pnr\example_design

rootfile1

把光纤两端接入SFP口(用户需购买光模块),进行Debugger在线调试,可看到窗口中发送和接收的数据一致的。

rootfile1

观察tx_data和rx0_data_align,两者一样说明收发没有问题。

7 以太网传输实验例程

7.1 实验简介

实验目的: 完成以太网通信测试。

实验环境: Window11 PDS2022.2-SP6.4

硬件环境: PG2L50H-484

7.2 实验原理

7.2.1 开发板以太网接口简介

开发板使用 裕泰微的 裕太微电子的YT8521SH-CA 实现了一个 10/100/1000 以太网端口。使用时需要用到光电转换模块,通过网线连接电脑的网口和光电转换模块的电口即可完成通信。

7.2.2 以太网协议简介

7.2.2.1 以太网帧格式
rootfile1

前导码(Preamble):8 字节,连续 7 个 8’h55 加 1 个 8’hd5,表示一个帧的开始,用于双方;设备数据的同步。

目的 MAC 地址:6 字节,存放目的设备的物理地址,即 MAC 地址;源 MAC 地址:6 字节,存放发送端设备的物理地址;

类型:2 字节,用于指定协议类型,常用的有 0800 表示 IP 协议,0806 表示 ARP 协议,8035 表示 RARP 协议;

数据:46 到 1500 字节,最少 46 字节,不足需要补全 46 字节,例如 IP 协议层就包含在数据部分,包括其 IP 头及数据。

FCS:帧尾,4 字节,称为帧校验序列,采用 32 位 CRC 校验,对目的 MAC 地址字段到数据字段进行校验。

进一步扩展,以 UDP 协议为例,可以看到其结构如下,除了以太网首部的 14 字节,数据部分包含 IP 首部,UDP 首部,应用数据共 46~1500 字节。

rootfile1
7.2.2.2 ARP 数据报格式

ARP 地址解析协议,即 ARP(Address Resolution Protocol),根据 IP 地址获取物理地址。主机发送包含目的 IP 地址的 ARP 请求广播(MAC 地址为 48’hff_ff_ff_ff_ff_ff)到网络上的主机,并接收返回消息,以此确定目标的物理地址,收到返回消息后将 IP 地址和物理地址保存到缓存中,并保留一段时间,下次请求时直接查询 ARP 缓存以节约资源。下图为 ARP 数据报格式。

rootfile1

帧类型:ARP 帧类型为两字节 0806;

硬件类型:指链路层网络类型,1 为以太网;

协议类型:指要转换的地址类型,采用 0x0800 IP 类型,之后的硬件地址长度和协议地址长度分别对应 6 和 4;

OP 字段中 1 表示 ARP 请求,2 表示 ARP 应答

例如:|ff ff ff ff ff ff|00 0a 35 01 fe c0|08 06|00 01|08 00|06|04|00 01|00 0a 35 01 fe c0|c0 a8 00 02| ff ff ff ff ff ff|c0 a8 00 03|

表示向 192.168.0.3 地址发送 ARP 请求。

|00 0a 35 01 fe c0 | 60 ab c1 a2 d5 15 |08 06|00 01|08 00|06|04|00 02| 60 ab c1 a2 d5 15|c0 a8 00 03|00 0a 35 01 fe c0|c0 a8 00 02|

表示向 192.168.0.2 地址发送 ARP 应答。

7.2.2.3 IP 数据包格式

因为 UDP 协议包只是 IP 包中的一种, 所以我们来介绍一下 IP 包的数据格式。下图为 IP分组的报文头格式,报文头的前 20 个字节是固定的,后面的可变

rootfile1

版本:占 4 位,指 IP 协议的版本目前的 IP 协议版本号为 4 (即 IPv4);

首部长度:占 4 位,可表示的最大数值是 15 个单位(一个单位为 4 字节)因此 IP 的首部长度的最大值是 60 字节;

区分服务:占 8 位,用来获得更好的服务,在旧标准中叫做服务类型,但实际上一直未被使用过 1998 年这个字段改名为区分服务.只有在使用区分服务(DiffServ)时,这个字段才起作用.一般的情况下都不使用这个字段;

总长度:占 16 位,指首部和数据之和的长度,单位为字节,因此数据报的最大长度为 65535字节总长度必须不超过最大传送单元 MTU

标识:占 16 位,它是一个计数器,用来产生数据报的标识

标志(flag):占 3 位,目前只有前两位有意义

MF标志字段的最低位是 MF (More Fragment),MF=1 表示后面“还有分片”。MF=0 表示最后一个分片

DF标志字段中间的一位是 DF (Don't Fragment),只有当 DF=0 时才允许分片。

片偏移:占 12 位,指较长的分组在分片后某片在原分组中的相对位置.片偏移以 8 个字节为偏移单位;

生存时间:占 8 位,记为 TTL (Time To Live) 数据报在网络中可通过的路由器数的最大值,TTL 字段是由发送端初始设置一个 8 bit 字段.推荐的初始值由分配数字 RFC 指定,当前值为 64.发送 ICMP 回显应答时经常把 TTL 设为最大值 255;

协议:占 8 位,指出此数据报携带的数据使用何种协议以便目的主机的 IP 层将数据部分上交给哪个处理过程, 1 表示为 ICMP 协议, 2 表示为 IGMP 协议, 6 表示为 TCP 协议, 17表示为 UDP 协议;

首部检验和:占 16 位,只检验数据报的首部不检验数据部分,采用二进制反码求和,即将16 位数据相加后,再将进位与低 16 位相加,直到进位为 0,最后将 16 位取反;

源地址和目的地址:都各占 4 字节,分别记录源地址和目的地址;

7.2.2.4 UDP 协议

UDP 是 User Datagram Protocol(用户数据报协议)的英文缩写。UDP 只提供一种基本的、低延迟的被称为数据报的通讯。所谓数据报,就是一种自带寻址信息,从发送端走到接收端的数据包。UDP 协议经常用于图像传输、网络监控数据交换等数据传输速度要求比较高的场合。

UDP 协议的报头格式:

UDP 报头由 4 个域组成,其中每个域各占用 2 个字节,具体如下:

rootfile1

① UDP 源端口号

② 目标端口号

③ 数据报长度

④ 校验和

UDP 协议使用端口号为不同的应用保留其各自的数据传输通道。数据发送一方将 UDP 数据报通过源端口发送出去,而数据接收一方则通过目标端口接收数据。

数据报的长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分(又称为数据负载)。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为 65535 字节。不过,一些实际应用往往会限制数据报的大小,有时会降低到 8192 字节。

UDP 协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此 UDP 协议可以检测是否出错。虽然 UDP 提供有错误检测,但检测到错误时,错误校正,只是简单地把损坏的消息段扔掉,或者给应用程序提供警告信息。

7.2.2.5 Ping 功能

UDP 协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此 UDP协议可以检测是否出错。虽然 UDP 提供有错误检测,但检测到错误时,错误校正,只是简单地把损坏的消息段扔掉,或者给应用程序提供警告信息。

rootfile1rootfile1

7.3 SMI(MDC/MDIO)总线接口

串行管理接口( Serial Management Interface ),也被称作 MII 管理接口( MII ManagementInterface),包括 MDC 和 MDIO 两条信号线。MDIO 是一个 PHY 的管理接口,用来读/写 PHY 的寄存器,以控制 PHY 的行为或获取 PHY 的状态,MDC 为 MDIO 提供时钟,由 MAC 端提供,在本实验中也就是 FPGA 端。在 RTL8211EG 文档里可以看到 MDC 的周期最小为 400ns,也就是最大时钟为 2.5MHz。

rootfile1

7.3.1 SMI 帧格式

如下图,为 SMI 的读写帧格式:

rootfile1
名称说明
Preamble由 MAC 发送 32 个连续的逻辑“1”,同步于 MDC 信号,用于 MAC 与 PHY 之间的同步;
ST帧开始位,固定为 01
OP操作码,10 表示读,01 表示写
PHYADPHY 的地址,5 bits
REGAD寄存器地址,5 bits
TATurn Around,MDIO 方向转换,在写状态下,不需要转换方向,值为 10,在读状态下,MAC 输出端为高阻态,在第二个周期,PHY 将 MDIO 拉低
DATA共 16bits 数据
IDLE空闲状态,此状态下 MDIO 为高阻态,由外部上拉电阻拉高

7.3.2 读时序

rootfile1

可以看到在 Turn Around 状态下,第一个周期 MDIO 为高阻态,第二个周期由 PHY 端拉低。

7.3.3 写时序

rootfile1

为了保证能够正确采集到数据,在 MDC 上升沿之前就把数据准备好,在本实验中为下降沿发送数据,上升沿接收数据。

7.4 实验设计

本实验以千兆以太网 RGMII 通信为例来设计 verilog 程序,会先发送预设的 UDP 数据到网络,每秒钟发送一次.程序分为两部分,分别为发送和接收,实现了 ARP,UDP 功能。

7.4.1 发送部分

7.4.1.1 MAC 层发送

发送部分中,mac_tx.v 为 MAC 层发送模块,首先在 SEND_START 状态,等待 mac_tx_ready信号,如果有效,表明 IP 或 ARP 的数据已经准备好,可以开始发送。再进入发送前导码状态,结束时发送 mac_data_req,请求 IP 或 ARP 的数据,之后进入发送数据状态,最后进入发送 CRC 状态。在发送数据过程中,需要同时进行 CRC 校验。前导码完成后就将上层协议数据发送出去,这个时候同样把这些上层数据放到 CRC32 模块中做序列生成,上层协议会给一个数据输出完成标志信号,这个时候 mac_tx 知道数据发送完成了,需要结束 CRC32 的序列生成,这个时候就开始提取 FCS,衔接数据之后发送出去。这样就连接了前导码---数据(Mac 帧)----FCS。之后跳转到结束状态,再回到 IDLE 状态,等待下一次的发送请求。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
mac_frame_datainput8从 IP 或 ARP 来的数据
mac_tx_reqinput1MAC 的发送请求
mac_tx_readyinput1IP 或 ARP 数据已准备好
mac_tx_endinput1IP 或 ARP 数据已经传输完毕
mac_tx_dataoutput8向 PHY 发送数据
mac_send_endoutput1MAC 数据发送结束
mac_data_validoutput1MAC 数据有效信号,即 gmii_tx_en
mac_data_reqoutput1MAC 层向 IP 或 ARP 请求数据
mac_tx_ackoutput1MAC 层发送数据的对于 UPPER 请求的应答
7.4.1.2 MAC 发送模式

工程中的 mac_tx_mode.v 为发送模式选择,根据发送模式是 IP 或 ARP 选择相应的信号与数据。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
mac_send_endinput1MAC 发送结束
arp_tx_reqinput1ARP 发送请求
arp_tx_readyinput1ARP 数据已准备好
arp_tx_datainput8ARP 数据
arp_tx_endinput1ARP 数据发送到 MAC 层结束
arp_tx_ackinput1ARP 发送响应信号
ip_tx_reqinput1IP 发送请求
ip_tx_readyinput1IP 数据已准备好
ip_tx_datainput8IP 数据
ip_tx_endinput1IP 数据发送到 MAC 层结束
mac_tx_readyoutput1MAC 数据已准备好信号
ip_tx_ackoutput1IP 发送响应信号
mac_tx_ackoutput1MAC 发送响应信号
mac_tx_reqoutput1MAC 发送请求
mac_tx_dataoutput8MAC 发送数据
mac_tx_endoutput1MAC 数据发送结束
7.4.1.3 ARP 发送

发送部分中,arp_tx.v 为 ARP 发送模块, 在 IDLE 状态下,等待 ARP 发送请求或 ARP应答请求信号,之后进入请求或应答等待状态,并通知 MAC 层,数据已经准备好,等待 mac_data_req 信号,之后进入请求或应答数据发送状态。由于数据不足 46 字节,需要补全46 字节发送。

rootfile1
信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
dest_mac_addrinput48发送的目的 MAC 地址
sour_mac_addrinput48发送的源 MAC 地址
sour_ip_addrinput32发送的源 IP 地址
dest_ip_addrinput32发送的目的 IP 地址
mac_data_reqinput1MAC 层请求数据信号
arp_request_reqinput1ARP 请求的请求信号
arp_reply_ackoutput1ARP 回复的应答信号
arp_reply_reqinput1ARP 回复的请求信号
arp_rec_sour_ip_addrinput32ARP 接收的源 IP 地址,回复时放到目的 IP 地址
arp_rec_sour_mac_addrinput48ARP 接收的源 MAC 地址,回复时放到目的 MAC 地址
mac_send_endinput1MAC 发送结束
mac_tx_ackinput1MAC 发送应答
arp_tx_readyoutput1ARP 数据准备好
arp_tx_dataoutput8ARP 发送数据
arp_tx_endoutput1ARP 数据发送结束
arp_tx_reqoutput1ARP 发送请求信号
7.4.1.4 IP 层发送

在发送部分,ip_tx.v 为 IP 层发送模块,在 IDLE 状态下,如果 ip_tx_req 有效,也就是 UDP 或 ICMP 发送请求信号,进入等待发送数据长度状态,之后进入产生校验和状态,校验和是将 IP 首部所有数据以 16 位相加,最后将进位再与低 16 位相加,直到进入为 0,再将低16 位取反,得出校验和结果。

在生成校验和之后,等待 MAC 层数据请求,开始发送数据,并在即将结束发送 IP 首部后请求 UDP 或 ICMP 数据。等发送完,进入 IDLE 状态。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
dest_mac_addrinput48发送的目的 MAC 地址
sour_mac_addrinput48发送的源 MAC 地址
sour_ip_addrinput32发送的源 IP 地址
dest_ip_addrinput32发送的目的 IP 地址
ttlinput8生存时间
ip_send_typeinput8上层协议号,如 UDP,ICMP
upper_layer_dataoutput8从 UDP 或 ICMP 过来的数据
upper_data_reqinput1向上层请求数据
mac_tx_ackinput1MAC 发送应答
mac_send_endinput1MAC 发送结束信号
mac_data_reqinput1MAC 层请求数据信号
upper_tx_readyinput1上层 UDP 或 ICMP 数据准备好
ip_tx_reqinput1发送请求,从上层过来
ip_send_data_lengthinput16发送数据总长度
ip_tx_ackoutput产生 IP 发送应答
ip_tx_readyoutput1IP 数据已准备好
ip_tx_dataoutput8IP 数据
ip_tx_endoutput1IP 数据发送到 MAC 层结束
7.4.1.5 IP 发送模式

工程中的 ip_tx_mode.v 为发送模式选择,根据发送模式是 UDP 或 ICMP 选择相应的信号与数据。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
mac_send_endinputMAC 数据发送结束
udp_tx_reqinput1UDP 发送请求
udp_tx_readyinput1UDP 数据准备好
udp_tx_datainput8UDP 发送数据
udp_send_data_lengthinput16UDP 发送数据长度
udp_tx_ackoutput1输出 UDP 发送应答
icmp_tx_reqinput1ICMP 发送请求
icmp_tx_readyinput1ICMP 数据准备好
icmp_tx_datainput8ICMP 发送数据
icmp_send_data_lengthinput16ICMP 发送数据长度
icmp_tx_ackoutput1ICMP 发送应答
ip_tx_ackinput1IP 发送应答
ip_tx_reqinput1IP 发送请求
ip_tx_readyoutput1IP 数据已准备好
ip_tx_dataoutput8IP 数据
ip_send_typeoutput8上层协议号,如 UDP,ICM
ip_send_data_lengthoutput16发送数据总长度
7.4.1.6 UDP 发送

发送部分中,udp_tx.v 为 UDP 发送模块。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
app_data_in_validinput1从外部所接收的数据输出有效信号
app_data_ininput8外部所接收的数据
app_data_lengthinput16从外部所接收的当前数据包的长度(不含 udp、ip、mac 首部)
udp_dest_portinput16从外部所接收的数据包的源端口号
app_data_requestinput1用户接口数据发送请求
udp_send_readyoutput1UDP 数据发送准备
udp_send_ackoutput1UDP 数据发送应当
ip_send_readyinput1IP 数据发送准备
ip_send_ackinput1IP 数据发送应当
udp_send_requestoutput1用户接口数据发送请求
udp_data_out_validoutput1发送的数据输出有效信号
udp_data_outoutput8发送的数据输出
udp_packet_lengthoutput16当前数据包的长度(不含 udp、 ip、mac 首部)

7.4.2 接收部分

7.4.2.1 MAC 层接收

在接收部分,其中 mac_rx.v 为 mac 层接收文件,首先在 IDLE 状态下当 rx_en 信号为高,进入 REC_PREAMBLE 前导码状态,接收前导码。之后进入接收 MAC 头部状态,即目的 MAC地址,源 MAC 地址,类型,将它们缓存起来,并在此状态判断前导码是否正确,错误则进入REC_ERROR 错误状态,在 REC_IDENTIFY 状态判断类型是 IP(8’h0800)或 ARP(8’h0806)。然后进入接收数据状态,将数据传送到 IP 或 ARP 模块,等待 IP 或 ARP 数据接收完毕,再接收 CRC 数据。并在接收数据的过程中对接收的数据进行 CRC 处理,将结果与接收到的 CRC 数据进行对比,判断数据是否接收正确,正确则结束,错误则进入 ERROR 状态。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
rx_eninput1开始接受使能
mac_rx_dataininput8接受的数据
checksum_errinput1IP 层校验错误信号
ip_rx_endinput1IP 接受结束
arp_rx_endinput1ARP 接受结束
ip_rx_reqoutput1IP 接受请求
arp_rx_reqinput1请求 ARP 接收
mac_rx_dataoutoutput8MAC 层接收数据输出给 IP 或 ARP
mac_rec_erroroutput1MAC 层接收错误
mac_rx_dest_mac_addroutput48MAC 接收的目的 IP 地址
mac_rx_sour_mac_addroutput48MAC 接收的源 IP 地址
7.4.2.2 ARP 接收

工程中的 arp_rx.v 为 ARP 接收模块,实现 ARP 数据接收,在 IDLE 状态下,接收到从 MAC层发来的 arp_rx_req 信号,进入 ARP 接收状态,在此状态下,提取出目的 MAC 地址,源 MAC地址,目的 IP 地址,源 IP 地址,并判断操作码 OP 是请求还是应答。如果是请求,则判断接收到的目的 IP 地址是否为本机地址,如果是,发送应答请求信号 arp_reply_req,如果不是,则忽略。如果 OP 是应答,则判断接收到的目的 IP 地址及目的 MAC 地址是否与本机一致,如果是,则拉高 arp_found 信号,表明接收到了对方的地址。并将对方的 MAC 地址及 IP 地址存入 ARP 缓存中。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
local_ip_addrinput32本地 IP 地址
local_mac_addrinput48本地 MAC 地址
arp_rx_datainput8ARP 接收数据
arp_rx_reqinput1ARP 接收请求
arp_rx_endoutput1ARP 接收完成
arp_reply_ackinput1ARP 回复应答
arp_reply_reqoutput1ARP 回复请求
arp_rec_sour_ip_addrinput32ARP 接收的源 IP 地址
arp_rec_sour_mac_addrinput48ARP 接收的源 MAC 地址
arp_foundoutput1ARP 接收到请求应答正确
7.4.2.3 IP 层接收模块

在工程中,ip_rx 为 IP 层接收模块,实现 IP 层的数据接收,信息提取,并进行校验和检查。首先在 IDLE 状态下,判断从 MAC 层发过来的 ip_rx_req 信号,进入接收 IP 首部状态,先在 REC_HEADER0 提取出首部长度及 IP 总长度,进入 REC_HEADER1 状态,在此状态提取出目的 IP 地址,源 IP 地址,协议类型,根据协议类型发送 udp_rx_req 或 icmp_rx_req。在接收首部的同时进行校验和的检查,将首部接收的所有数据相加,存入 32 位寄存器,再将高 16 位

与低 16 位相加,直到高 16 位为 0 ,再将低 16 位取反,判断其是否为 0,如果是 0,则检验正确,否则错误,进入 IDLE 状态,丢弃此帧数据,等待下次接收。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
local_ip_addrinput32本地 IP 地址
local_mac_addrinput48本地 MAC 地址
ip_rx_datainput8从 MAC 层接收的数据
ip_rx_reqinput1MAC 层发送的 IP 接收请求信号
mac_rx_dest_mac_addrinput48MAC 层接收的目的 MAC 地址
udp_rx_reqoutput1UDP 接收请求信号
icmp_rx_reqoutput1ICMP 接收请求信号
ip_addr_check_erroroutput1地址检查错误信号
upper_layer_data_lengthoutput16上层协议的数据长度
ip_total_data_lengthoutput16数据总长度
net_protocoloutput8网络协议号
ip_rec_source_addroutput32IP 层接收的源 IP 地址
ip_rec_dest_addroutput32IP 层接收的目的 IP 地址
ip_rx_endoutput1IP 层接收结束
ip_checksum_erroroutput1IP 层校验和检查错误信号
7.4.2.4 UDP 接收

在工程中,udp_rx.v 为 UDP 接收模块,在此模块首先接收 UDP 首部,再接收数据部分,在接收的同时进行 UDP 校验和检查,如果 UDP 数据是奇数个字节,在计算校验和时,在最后一个字节后加上 8’h00,并进行校验和计算。校验方法与 IP 校验和一样,如果校验正确,将拉高 udp_rec_data_valid 信号,表明接收的 UDP 数据有效,否则无效,等待下次接收。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
udp_rx_datainput8UDP 接收数据
udp_rx_reqinput1UDP 接收请求
ip_checksum_errorinput1IP 层校验和检查错误信号
ip_addr_check_errorinput1地址检查错误信号
udp_rec_rdataoutput8UDP 接收读数据
udp_rec_data_lengthoutput16UDP 接收数据长度
udp_rec_data_validoutput1UDP 接收数据有效

7.4.3 其他部分

7.4.3.1 ICMP 应答

在工程中,icmp_reply.v 实现 ping 功能,首先接收其他设备发过来的 icmp 数据,判断类型是否是回送请求(ECHO REQUEST),如果是,将数据存入 RAM,并计算校验和,判断校验和是否正确,如果正确则进入发送状态,将数据发送出去。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
mac_send_endinput1Mac 发送结束信号
ip_tx_ackinput1IP 发送应答
icmp_rx_datainput8ICMP 接收数据
icmp_rx_reqinput1ICMP 接收请求
icmp_rev_errorinput1接收错误信号
upper_layer_data_lengthinput16上层协议长度
icmp_data_reqinput1请求 ICMP 数据
icmp_tx_readyoutput1ICMP 发送准备好
icmp_tx_dataoutput8ICMP 发送数据
icmp_tx_endoutput1ICMP 发送结束
icmp_tx_reqoutput1ICMP 发送请求
7.4.3.2 ARP 缓存

在工程中,arp_cache.v 为 arp 缓存模块,将接收到的其他设备 IP 地址和MAC 地址缓存,在发送数据之前,查询目的地址是否存在,如果不存在,则向目的地址发送 ARP 请求,等待应答。在设计文件中,只做了一个缓存空间,如果有需要,可扩展。

信号名称方向位宽说明
clkinput1系统时钟
rstninput1低电平复位
arp_foundinput1ARP 接收到回复正确
arp_rec_source_ip_addrinput32ARP 接收的源 IP 地址
arp_rec_source_mac_addrinput48ARP 接收的源 MAC 地址
dest_ip_addrinput32目的 IP 地址
dest_mac_addroutput48目的 MAC 地址
mac_not_existoutput1目的地址对应的 MAC 地址不存在
7.4.3.3 CRC 校验模块(crc.v)

CRC32 校验是在目标 MAC 地址开始计算的,一直计算到一个包的最后一个数据为止。一些网站可以自动生成 CRC 算法的 verilog 文件:https://bues.ch/cms/hacking/crcgen.html

rootfile1

7.5 实验现象

用光电转换模块插入SFP口,再用一根网线和 PC 端网口相连;

设置接收端(PC 端)IP 地址为 192.168.0.3,开发板的 IP 地址为 192.168.0.2 如下图:

rootfile1

通过命令提示符,输入 arp -a,可以查到 IP:192.168.0.2 MAC:a0_b1_c2_d3_e1_e1;

rootfile1

通过 Wireshark 软件抓包验证数据链路是否正常连接以及数据传输是否正常。资料包中PC 端打开 Wireshark 软件,烧录重新后进行捕获数据报可以看到如下所示的交互过程。

成功建立连接后会持续发送数据报“www.meyesemi.com”。如下所示:

rootfile1rootfile1

Ping功能测试,由上图可知,ping基本不丢包。

在 GitHub 上编辑此页
上次更新:
贡献者: zwhuang
Prev
ARM与FPGA通讯