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

  本文设计思路源自明德扬至简设计法。在前面的几乎首博文被,由于设计比较简单,所有的效益还是故verilogHDL代码编写实现的。我们而学会站于巨人之肩上,这时候就该IP核登场了!

  说白了,IP核就是别人做好了之硬件模块,提供整机的用户接口及验证文档,更复杂的还有示例工程,你如能够因此好这个IP核,设计已到位一半了。说起来容易,从长的英文文档和网上各个非标准教程被汲取所需要,并灵活运用还是用下一番素养之。

  我看其中最为着重的几乎碰如下:

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

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

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

  4)
知道其中寄存器地址及效果与配置方式、顺序;

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

  今天来讲说一个顶常用的IP核,FIFO。可以说她是FPGA能这么灵活处理数据的根底,常用来异步时钟域处理、位富有转换与要多少缓存的场子。先来说明下,对于新家和正好点一个IP核的丁来说,不要过分关注IP核的各一个参数与作用,更不曾必要知道里面的现实性组织与劳作原理(还没忘记之前以的ILA吧,反正自己是休明了具体怎么设计下的)只需要掌握最常用之同极端要的,把IP核用起来就是可怜功告成了。先由生成IP核开始吧:

图片 1

  配置向导中第一页中凡是挑FIFO的接口模式和实现方式。这里我们为此旧的接口方式。箭头处是贯彻方式,如果用异步时钟域处理选择读写独立钟模式。

图片 2

  第二页中需特别强调的是朗诵模式的挑三拣四。其实这里的First
Word Fall Through对应之虽是Altera FPGA中FIFO IP核读模式遭遇之Show
ahead模式嘛,换个名字而已。这个读模式的特性是在念而能管用之前,即把FIFO中第一个数据由读数据端口不断送出。在这种模式下,读而能信号倒像是“读清”信号,把上同次的数清除掉,让FIFO送出下一个多少。这样做的处于是切合dout
和dout_vld相兼容的输出信号方式。

图片 3

  第三页是布局有只是选的标志位,可以依据需要巧实现部分标志位和抓手特性(我是从来没因此过)。

图片 4

  第四页可选FIFO内缓存数据量计数器,由于自家开选择的凡异步FIFO模式,所以这处起点儿只计数器分别与读侧和写侧时钟上升沿同。注意一点:这片只计数器均代表FIFO缓存数据量,只不过在钟上稍加病,切不可错误理解呢是写副了还是读来了不怎么个数据。

图片 5

  最后总结页,把前的参数与布局汇总下。没有问题可点击OK了!

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

图片 6

  为简化设计,先就考虑针对封包后数添加MAC头部的效力,也就是说此时输入的数目就是凡是长符合以太网规范,且所有数据包格式的多少。由于当多少有输出前加额外之音,所以先要缓存输入的数直到MAC头输出完成再以写副数据发送出,因此需要为此FIFO缓存数据。进一步分析,经过封包后的多寡格式如下:

图片 7

  其中sop和eop分别是包头,包尾指示信号,data_vld是数量中指示信号。由于数量位富有此处是32各类,而数的最小单元是字节,所以每个32个数据不必然带有4个字节有效数据,使用data_mod指示出无效字节数。为了吃该模块输出端知道何时输出了一个数据包,要将eop信号和数据信号拼接写副FIFO中,这样输出端发出eop时入新一车轮循环。如果因写副sop信号来作初始发送MAC头部和数量有的标志,试想当一个短包紧跟着一个添加包写进FIFO中时,输出端正在送出高达一致长管剩下的几独数据,无法响应短包的sop信号指示,那么短包即吃“丢弃”了。为了避免丢包现象,需要满足“读写隔离规则”,即FIFO读操作和描绘操作两者不可知因同样正在的情形来决定另外一样在的行。进一步引出“双FIFO架构”,使用数据FIFO缓存数据,而信息FIFO保留指示信息,这样说道写侧的指令信号写副信息FIFO中,数据FIFO可以依据信息FIFO读侧的消息来判断读之行,也就满足了读写隔离规则。

图片 8

  以拖欠模块中,可以当写侧出现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信号来区别上述两栽情况。需要特别注意的凡,在运动操作后数包中包含的废字节个数也会发生变化。为了理清思路以及时序,画生中心信号时先后图:

图片 9

图片 10

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 

  有了型要求,设计思路后肯定模块接口列表:

 图片 11

 

  开始修代码了:

  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任务即可实现所有不同长短,不同无效字节个数的数据包。观察输出波形:

图片 12

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

图片 13

 

 

 

图片 14

图片 15

 

 

 

   可以看mac侧数据发送正确。本博文由于要描述FIFO应用,这里仅做出行为仿真,读者可灵活运用,添加在友好之档次遭到。