转载:

说白了,IP核就是别人做好了的硬件模块,提供完整的用户接口和说明文档,更复杂的还有示例工程,你只要能用好这个IP核,设计已经完成一半了。说起来容易,从冗长的英文文档和网上各个非标准教程中汲取所需,并灵活运用还是需要下一番功夫的。

  我认为其中最重要的几点如下:

  1) 提供给IP核正确的时钟和复位条件;

  2) 明确各个重要用户接口功能;

  3) 掌握所需指令的操作时序;

  4) 知道内部寄存器地址及功能和配置方式、顺序;

  5) 会从官方示例工程中学会IP核正确使用方式;

  今天来讲讲一个最常用的IP核,FIFO。可以说它是FPGA能如此灵活处理数据的基础,常用于异步时钟域处理、位宽转换以及需要数据缓存的场合。先来说明下,对于初学者和刚接触一个IP核的人来说,不要过分关注IP核的每一个参数和功能,更没必要知道内部的具体结构和工作原理(还没忘之前使用的ILA吧,反正我是不知道具体怎么设计出来的)只需掌握最常用的和最重要的,把IP核用起来就大功告成了。先从生成IP核开始吧:

  配置向导中第一页中是选择FIFO的接口模式和实现方式。这里我们用原始的接口方式。箭头处是实现方式,如果需要异步时钟域处理选择读写独立时钟模式。

  第二页中需要特意强调的是读模式的选择。其实这里的First Word Fall Through对应的就是Altera FPGA中FIFO IP核读模式中的Show ahead模式嘛,换个名字而已。这个读模式的特点是在读使能有效之前,即把FIFO中第一个数据从读数据端口持续送出。在这种模式下,读使能信号倒像是“读清”信号,把上一次的数据清除掉,让FIFO送出下一个数据。这样做的处是符合dout 和dout_vld相配合的输出信号方式。

  第三页是配置一些可选的标志位,可以根据需要灵活实现一些标志位和握手特性(我是从来没用过)。

  第四页可选FIFO内缓存数据量计数器,由于我开始选择的是异步FIFO模式,所以此处有两个计数器分别与读侧和写侧时钟上升沿同步。注意一点:这两个计数器均表示FIFO缓存数据量,只不过在时钟上有些偏差,切不可错误理解为是写入了或者读出了多少个数据。

  最后总结页,把前边的参数和配置汇总下。没有问题可以点击OK了!

  IP核生成好了,接下来要正确用起来。我们把以太网接口数据传输作为案例背景,通常来说是FPGA逻辑+MAC IP核+外部PHY芯片的架构。若想让MAC IP核正确接收待发送数据,需要将数据进行封包并加入MAC头部信息。

  为简化设计,先只考虑对封包后数据添加MAC头部的功能,也就是说此时输入的数据即是长度符合以太网规范,且具有数据包格式的数据。由于在数据部分输出前加额外的信息,所以先要缓存输入的数据直到MAC头输出完成再将写入数据发送出来,因此需要用FIFO缓存数据。进一步分析,经过封包后的数据格式如下:

  其中sop和eop分别是包头,包尾指示信号,data_vld是数据有效指示信号。由于数据位宽此处是32位,而数据的最小单元是字节,所以每个32位数据不一定包含4个字节有效数据,使用data_mod指示出无效字节数。为了让该模块输出端知道何时输出完一个数据包,要把eop信号和数据信号拼接写入FIFO中,这样输出端发出eop时进入新一轮循环。如果根据写入sop信号来作为开始发送MAC头部和数据部分的标志,试想当一个短包紧跟着一个长包写进FIFO中时,输出端正在送出上一长包剩下的几个数据,无法响应短包的sop信号指示,那么短包即被“丢弃”了。为了避免丢包现象,需要满足“读写隔离规则”,即FIFO读操作和写操作两者不能根据一方的情况来决定另一方的行为。进一步引出“双FIFO架构”,使用数据FIFO缓存数据,而信息FIFO保留指示信息,这样讲写侧的指示信号写入信息FIFO中,数据FIFO可以根据信息FIFO读侧的信息来判断读的行为,也就满足了读写隔离规则。

  在该模块中,可以在写侧出现sop信号时写入信息FIFO一个指示信息,所以当信息FIFO非空即表示有一个数据包正在进来,此时发送MAC头信息,随之读取数据FIFO中缓存数据,当读侧出现eop信号则读清信息FIFO,循环往复完成了添加头部信息的工作。

  MAC头部信息为14字节,而数据位宽是32位,即一次发送四个字节,所以相当于头部为三个半数据。因此在发送第三个头部数据时,低16位要用数据部分填充,后边的数据也要跟着移位。如此移位操作后,数据部分就晚了一拍输出最后16位。如果最后这16位数据中有有效字节,那么mac_data_vld当前节拍也要有效,且mac_data_eop和mac_data_mod跟着晚一拍输出;如果无有效字节,则按照正常情况输出。在代码中我使用end_normal和end_lag信号来区分上述两种情况。需要特别注意的是,在移位操作后数据包中包含的无效字节个数也会发生变化。为了理清思路和时序,画出核心信号时序图:

有了项目需求,设计思路后明确模块接口列表:

  开始编写代码了:

  1 `timescale 1ns / 1ps
2
3 module add_mac_head(
4 input clk,
5 input rst_n,
6 input [31:0] app_data,
7 input app_data_vld,
8 input app_data_sop,
9 input app_data_eop,
10 input [1:0] app_data_mod,//无效字节数
11
12 input mac_tx_rdy,//MAC IP发送准备就绪信号
13 output reg [31:0] mac_data,
14 output reg mac_data_vld,
15 output reg mac_data_sop,
16 output reg mac_data_eop,
17 output reg [1:0] mac_data_mod
18 );
19
20 reg [34:0] wdata;
21 reg wrreq,rdreq;
22 reg wdata_xx;
23 reg wrreq_xx,rdreq_xx;
24 reg [1:0] head_cnt;
25 reg head_flag,head_tmp,rd_flag,rd_flag_tmp;
26 reg [34:0] q_tmp;
27
28 wire [31:0] data_shift;
29 wire add_head_cnt,end_head_cnt;
30 wire head_neg;
31 wire [34:0] q;
32 wire rdempty_xx;
33 wire sop_in;
34 wire [2:0] head_len;
35 wire [111:0] mac_head;
36 wire [47:0] des_mac,sour_mac;
37 wire [15:0] pack_type;
38 wire rd_neg;
39
40 fifo_generator_0 fifo_data (
41 .clk(clk), // input wire clk
42 .din(wdata), // input wire [34 : 0] din
43 .wr_en(wrreq), // input wire wr_en
44 .rd_en(rdreq), // input wire rd_en
45 .dout(q), // output wire [34 : 0] dout
46 .full(), // output wire full
47 .empty() // output wire empty
48 );
49
50 fifo_generator_1 fifo_message (
51 .clk(clk), // input wire clk
52 .din(wdata_xx), // input wire [0 : 0] din
53 .wr_en(wrreq_xx), // input wire wr_en
54 .rd_en(rdreq_xx), // input wire rd_en
55 .dout(), // output wire [0 : 0] dout
56 .full(), // output wire full
57 .empty(rdempty_xx) // output wire empty
58 );
59
60 //数据fifo写数据
61 always@(posedge clk or negedge rst_n)begin
62 if(!rst_n)
63 wdata <= 0;
64 else if(app_data_vld)
65 wdata <= {app_data_eop,app_data_mod,app_data};
66 end
67
68 always@(posedge clk or negedge rst_n)begin
69 if(!rst_n)
70 wrreq <= 0;
71 else if(app_data_vld)
72 wrreq <= 1;
73 else
74 wrreq <= 0;
75 end
76
77 always@(posedge clk or negedge rst_n)begin
78 if(!rst_n)
79 wdata_xx <= 0;
80 else if(sop_in)
81 wdata_xx <= 1;
82 else
83 wdata_xx <= 0;
84 end
85
86 assign sop_in = app_data_vld && app_data_sop;
87
88 //当写侧出现sop时表明有一个数据包正在写入,此时写信息FIFO任意数据告知读侧开始发送MAC头部信息
89 always@(posedge clk or negedge rst_n)begin
90 if(!rst_n)
91 wrreq_xx <= 0;
92 else if(sop_in)
93 wrreq_xx <= 1;
94 else
95 wrreq_xx <= 0;
96 end
97
98 //MAC头部有14个字节 数据位宽是32位,即一个数据4个字节,需要发送4个数据(最后一个数据只有2个字节是头部)
99 always@(posedge clk or negedge rst_n)begin
100 if(!rst_n)
101 head_cnt <= 0;
102 else if(add_head_cnt)begin
103 if(end_head_cnt)
104 head_cnt <= 0;
105 else
106 head_cnt <= head_cnt + 1'b1;
107 end
108 end
109
110 assign add_head_cnt = head_flag && mac_tx_rdy;
111 assign end_head_cnt = add_head_cnt && head_cnt == head_len - 1 - 1;
112 assign head_len = 4;
113
114 //发送MAC头部标志位
115 always@(posedge clk or negedge rst_n)begin
116 if(!rst_n)
117 head_flag <= 0;
118 else if(end_head_cnt)
119 head_flag <= 0;
120 else if(!rdempty_xx && !rd_flag)
121 head_flag <= 1;
122 end
123
124 //读数据FIFO标志位
125 always@(posedge clk or negedge rst_n)begin
126 if(!rst_n)
127 rd_flag <= 0;
128 else if(end_head_cnt)
129 rd_flag <= 1;
130 else if(rd_eop)
131 rd_flag <= 0;
132 end
133
134 assign rd_eop = rdreq && q[34];
135
136 always@(*)begin
137 if(rd_flag && mac_tx_rdy)
138 rdreq <= 1;
139 else
140 rdreq <= 0;
141 end
142
143 //读侧出现eop读取完整版报文,此时读清信息FIFO
144 always@(*)begin
145 if(rd_eop)
146 rdreq_xx <= 1;
147 else
148 rdreq_xx <= 0;
149 end
150
151 //寄存头部标志位找出下降沿
152 always@(posedge clk or negedge rst_n)begin
153 if(!rst_n)
154 head_tmp <= 0;
155 else
156 head_tmp <= head_flag;
157 end
158
159 assign head_neg = head_flag == 0 && head_tmp == 1;
160
161 //寄存q用于移位操作
162 always@(posedge clk or negedge rst_n)begin
163 if(!rst_n)
164 q_tmp <= 0;
165 else
166 q_tmp <= q;
167 end
168
169 assign data_shift = {q_tmp[15:0],q[31:16]};
170
171 //MAC头 14字节
172 assign mac_head = {des_mac,sour_mac,pack_type};
173 assign des_mac = 48'hD0_17_C2_00_E5_40 ;//目的MAC PC网卡物理地址
174 assign sour_mac = 48'h01_02_03_04_05_06 ;//源MAC地址为01_02_03_04_05_06
175 assign pack_type = 16'h0800 ;//IP数据报
176
177 always@(posedge clk or negedge rst_n)begin
178 if(!rst_n)
179 rd_flag_tmp <= 0;
180 else
181 rd_flag_tmp <= rd_flag;
182 end
183
184 assign rd_neg = rd_flag == 0 && rd_flag_tmp == 1;
185
186 //数据输出
187 always@(posedge clk or negedge rst_n)begin
188 if(!rst_n)
189 mac_data_sop <= 0;
190 else if(add_head_cnt && head_cnt == 0)
191 mac_data_sop <= 1;
192 else
193 mac_data_sop <= 0;
194 end
195
196 always@(posedge clk or negedge rst_n)begin
197 if(!rst_n)
198 mac_data_eop <= 0;
199 else if(end_normal || end_lag)
200 mac_data_eop <= 1;
201 else
202 mac_data_eop <= 0;
203 end
204
205 assign end_normal = rd_eop && q[33:32] > 2'd1;
206 assign end_lag = rd_neg && q_tmp[33:32] <= 2'd1;
207
208 always@(posedge clk or negedge rst_n)begin
209 if(!rst_n)
210 mac_data <= 0;
211 else if(add_head_cnt)//由于MAC不是32位数据的整数倍,需要对数据进行移位
212 mac_data <= mac_head[111 - head_cnt*32 -: 32];
213 else if(head_neg)
214 mac_data <= {mac_head[15:0],q[31:16]};
215 else
216 mac_data <= data_shift;
217 end
218
219 always@(posedge clk or negedge rst_n)begin
220 if(!rst_n)
221 mac_data_vld <= 0;
222 else if(head_flag || rd_flag || end_lag)
223 mac_data_vld <= 1;
224 else
225 mac_data_vld <= 0;
226 end
227
228 //输出无效字节个数 由于输出端进行了数据移位,导致无效数据个数发生变化
229 always@(posedge clk or negedge rst_n)begin
230 if(!rst_n)
231 mac_data_mod <= 0;
232 else if(end_normal)
233 mac_data_mod <= q[33:32] - 2;
234 else if(end_lag && q_tmp[33:32] == 2'd1)
235 mac_data_mod <= 1;
236 else if(end_lag && q_tmp[33:32] == 0)
237 mac_data_mod <= 2;
238 else
239 mac_data_mod <= 0;
240 end
241
242 endmodule

编写测试激励验证功能:

 1 `timescale 1ns / 1ps
2
3 module add_mac_head_tb;
4
5
6 reg clk,rst_n;
7 reg [31:0] app_data;
8 reg app_data_sop,app_data_eop,app_data_vld;
9 reg [1:0] app_data_mod;
10 reg mac_tx_rdy;
11
12 wire [31:0] mac_data;
13 wire mac_data_vld,mac_data_sop,mac_data_eop;
14 wire [1:0] mac_data_mod;
15
16 add_mac_head add_mac_head(
17 .clk(clk),
18 .rst_n(rst_n),
19 .app_data(app_data),
20 .app_data_vld(app_data_vld),
21 .app_data_sop(app_data_sop),
22 .app_data_eop(app_data_eop),
23 .app_data_mod(app_data_mod),//无效字节数
24
25 .mac_tx_rdy(mac_tx_rdy),//MAC IP发送准备就绪信号
26 .mac_data(mac_data),
27 .mac_data_vld(mac_data_vld),
28 .mac_data_sop(mac_data_sop),
29 .mac_data_eop(mac_data_eop),
30 .mac_data_mod(mac_data_mod)
31 );
32
33 parameter CYC = 5,
34 RST_TIME = 2;
35
36 integer i;
37
38 initial begin
39 clk = 1;
40 forever #(CYC / 2.0) clk = ~clk;
41 end
42
43 initial begin
44 rst_n = 1;
45 #1;
46 rst_n = 0;
47 #(CYC*RST_TIME);
48 rst_n = 1;
49 end
50
51 initial begin
52 #1;
53 app_data = 0;
54 app_data_sop = 0;
55 app_data_eop = 0;
56 app_data_mod = 0;
57 app_data_vld = 0;
58 mac_tx_rdy = 1;
59 #(CYC*RST_TIME);
60 packet_gen(10,0);
61 packet_gen(5,0);
62 packet_gen(15,2);
63 #1000;
64 $stop;
65 end
66
67 task packet_gen;
68 input [15:0] length;
69 input [1:0] invld_num;
70 begin
71 app_data_vld = 1;
72 app_data_sop = 1;
73 app_data = 32'h01020300;
74 for(i = 0;i < length;i = i + 1'b1)begin
75 if(i == 1)
76 app_data_sop = 0;
77 else if(i == length - 1)begin
78 app_data_mod = invld_num;
79 app_data_eop = 1;
80 end
81 app_data = app_data +1'b1;
82 #(CYC*1);
83 end
84 app_data_eop = 0;
85 app_data_vld = 0;
86 app_data_mod = 0;
87 end
88 endtask
89
90 endmodule

  连续输入三个长度不同的报文,此处为了设计重用,用可参数化的task对激励报文进行封装。需要输入报文时只需调用packet_gen任务即可实现具有不同长度,不同无效字节个数的数据包。观察输出波形:

mac侧输出三个包文数据如下:

  可以看出mac侧数据发送正确。本博文由于主要讲述FIFO应用,这里只做出行为仿真,读者可以灵活运用,添加在自己的项目中。

FIFO IP核的更多相关文章

  1. FIFO IP核仿真

    FIFO IP核仿真 1.FIFO IP核配置 2.FIFO测试逻辑代码 首先往FIFO里面写入512个数据(FIFO深度的一半),然后再开始同时往FIFO里面写入,读出数据.FIFO读和写的时钟域不 ...

  2. Altera FIFO IP核时序说明

    ALTERA在LPM(library of parameterized mudules)库中提供了参数可配置的单时钟FIFO(SCFIFO)和双时钟FIFO(DCFIFO).FIFO主要应用在需要数据 ...

  3. FPGA基础学习(2) -- FIFO IP核(Quartus)

    ALTERA在LPM(library of parameterized mudules)库中提供了参数可配置的单时钟FIFO(SCFIFO)和双时钟FIFO(DCFIFO).FIFO主要应用在需要数据 ...

  4. IP核之初——FIFO添加以太网MAC头部

    本文设计思路源自明德扬至简设计法.在之前的几篇博文中,由于设计比较简单,所有的功能都是用verilogHDL代码编写实现的.我们要学会站在巨人的肩膀上,这时候就该IP核登场了! 说白了,IP核就是别人 ...

  5. IP核——FIFO

    一.Quartus 1.打开Quartus ii,点击Tools---MegaWizard Plug-In Manager 2.弹出创建页面,选择Creat a new custom megafunc ...

  6. Lattice 的 Framebuffer IP核使用调试笔记之IP核生成与参数设置

    本文由远航路上ing 原创,转载请标明出处. 这节笔记记录IP核的生成以及参数设置. 先再IP库里下载安装Framebuffer 的ipcore 并安装完毕. 一.IP核的生成: 1.先点击IP核则右 ...

  7. 7 Series GTP IP核使用总结 IP核配置篇

    FPGA内嵌收发器相当于以太网中的PHY芯片,但更灵活更高效,线速率也在随着FPGA芯片的发展升级.本文对7系列FPGA内部高速收发器GTP IP核的配置和使用做些简单的总结,以备后续回顾重用.本文是 ...

  8. 浅析Xilinx 三速以太网MAC IP核

    之前在使用Altera的三速以太网MAC IP的基础上,完成了UDP协议数据传输.此次为了将设计移植到xilinx FPGA上,需要用到xilinx的三速以太网MAC IP核,当然也可以自己用HDL编 ...

  9. xilinx IP核配置,一步一步验证Xilinx Serdes GTX最高8.0Gbps

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010161493/article/details/77658599   目录(?)[+]   之前 ...

随机推荐

  1. 【Python】读取excel数据

    '''python3读取excle数据''' import xlrd workbook = xlrd.open_workbook(r'test.xls', encoding_override='gbk ...

  2. 如何在 Azure 中均衡 Windows 虚拟机负载以创建具有高可用性的应用程序

    负载均衡通过将传入请求分布到多个虚拟机来提供更高级别的可用性. 本教程介绍了 Azure 负载均衡器的不同组件,这些组件用于分发流量和提供高可用性. 你将学习如何执行以下操作: 创建 Azure 负载 ...

  3. python web编程CGI

    CGI(通用网关接口),CGI 是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能. CGI 应用程序能与浏览器进行交互,还可通过数据库API 与数据库服务器等外部数据源进行 ...

  4. 【转】Spring学习---为什么要用spring,springMVC

    [原文]https://www.toutiao.com/i6593182323095634445/ 首先,软件里有很多优秀的框架,有一种类型的框架,它的特点是建立在一个现有技术的基础上,提供和现有技术 ...

  5. apk静态注射[转]-未实践

    原文:http://free0coding.iteye.com/blog/1684263 1.将需要注入的代码块打包成jar1,释放一个公共类的静态方法a  2.反编译apk得到smali文件,在适当 ...

  6. October 10th 2017 Week 41st Tuesday

    If you focus on what you left behind you will never see what lies ahead. 如果你只顾回头看,那么你永远也看不见前方有什么. Ye ...

  7. 极限编程核心价值:沟通(Communication)

    原文:https://deviq.com/communication 极限编程核心价值:简单(Simplicity) 极限编程核心价值:沟通(Communication) 极限编程核心价值:反馈(Fe ...

  8. 使用SuperMap对接天地图

    我们在做项目时,经常需要与天地图对接,对接形式分为2种: 1. 将公网天地图用作项目底图,在JavaScript客户端加载显示: 2. 将自己发布的WMTS地图服务给别人用,同时需要自己的服务能和天地 ...

  9. linux ssh 应用

    linux 服务器 连接另一个linux服务器 ssh 用户名@IP地址 linux 服务器传输文件到另一个linux服务器 scp 文件名(可多个)  用户名@IP地址:传到的目录 /home

  10. CentOS7.5服务器安装(并添加用户) anaconda3 并配置 PyTorch1.0

    ===========================================================================================[admin@lo ...