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

  说白了,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信号来区分上述两种情况。需要特别注意的是,在移位操作后数据包中包含的无效字节个数也会发生变化。为了理清思路和时序,画出核心信号时序图:

  

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

  开始编写代码了:

 `timescale 1ns / 1ps

 module add_mac_head(
     input clk,
     input rst_n,
     :] app_data,
     input app_data_vld,
     input app_data_sop,
     input app_data_eop,
     :] app_data_mod,//无效字节数

     input mac_tx_rdy,//MAC IP发送准备就绪信号
     :] mac_data,
     output reg mac_data_vld,
     output reg mac_data_sop,
     output reg mac_data_eop,
     :] mac_data_mod
     );

     :] wdata;
     reg wrreq,rdreq;
     reg wdata_xx;
     reg wrreq_xx,rdreq_xx;
     :] head_cnt;
     reg head_flag,head_tmp,rd_flag,rd_flag_tmp;
     :] q_tmp;

     :] data_shift;
     wire add_head_cnt,end_head_cnt;
     wire head_neg;
     :] q;
     wire rdempty_xx;
     wire sop_in;
     :] head_len;
     :] mac_head;
     :] des_mac,sour_mac;
     :] pack_type;
     wire rd_neg;

     fifo_generator_0 fifo_data (
   .clk(clk),      // input wire clk
   .din(wdata),      // input wire [34 : 0] din
   .wr_en(wrreq),  // input wire wr_en
   .rd_en(rdreq),  // input wire rd_en
   .dout(q),    // output wire [34 : 0] dout
   .full(),    // output wire full
   .empty()  // output wire empty
 );

     fifo_generator_1 fifo_message (
   .clk(clk),      // input wire clk
   .din(wdata_xx),      // input wire [0 : 0] din
   .wr_en(wrreq_xx),  // input wire wr_en
   .rd_en(rdreq_xx),  // input wire rd_en
   .dout(),    // output wire [0 : 0] dout
   .full(),    // output wire full
   .empty(rdempty_xx)  // output wire empty
 );

     //数据fifo写数据
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             wdata <= ;
         else if(app_data_vld)
             wdata <= {app_data_eop,app_data_mod,app_data};
     end

     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             wrreq <= ;
         else if(app_data_vld)
             wrreq <= ;
         else
             wrreq <= ;
     end

     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             wdata_xx <= ;
         else if(sop_in)
             wdata_xx <= ;
         else
             wdata_xx <= ;
     end

     assign sop_in = app_data_vld && app_data_sop;

     //当写侧出现sop时表明有一个数据包正在写入,此时写信息FIFO任意数据告知读侧开始发送MAC头部信息
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             wrreq_xx <= ;
         else if(sop_in)
             wrreq_xx <= ;
         else
             wrreq_xx <= ;
     end

     //MAC头部有14个字节 数据位宽是32位,即一个数据4个字节,需要发送4个数据(最后一个数据只有2个字节是头部)
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             head_cnt <= ;
         else if(add_head_cnt)begin
             if(end_head_cnt)
                 head_cnt <= ;
             else
                 head_cnt <= head_cnt + 'b1;
         end
     end

     assign add_head_cnt = head_flag && mac_tx_rdy;
      - ;
     ;

     //发送MAC头部标志位
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             head_flag <= ;
         else if(end_head_cnt)
             head_flag <= ;
         else if(!rdempty_xx && !rd_flag)
             head_flag <= ;
     end

     //读数据FIFO标志位
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             rd_flag <= ;
         else if(end_head_cnt)
             rd_flag <= ;
         else if(rd_eop)
             rd_flag <= ;
     end

     ];

     always@(*)begin
         if(rd_flag && mac_tx_rdy)
             rdreq <= ;
         else
             rdreq <= ;
     end

     //读侧出现eop读取完整版报文,此时读清信息FIFO
     always@(*)begin
         if(rd_eop)
             rdreq_xx <= ;
         else
             rdreq_xx <= ;
     end

     //寄存头部标志位找出下降沿
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             head_tmp <= ;
         else
             head_tmp <= head_flag;
     end

      && head_tmp == ;

     //寄存q用于移位操作
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             q_tmp <= ;
         else
             q_tmp <= q;
     end

     :],q[:]};

     //MAC头 14字节
     assign mac_head  = {des_mac,sour_mac,pack_type};
     'hD0_17_C2_00_E5_40  ;//目的MAC PC网卡物理地址
     'h01_02_03_04_05_06  ;//源MAC地址为01_02_03_04_05_06
     'h0800               ;//IP数据报

     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             rd_flag_tmp <= ;
         else
             rd_flag_tmp <= rd_flag;
     end

      && rd_flag_tmp == ;

     //数据输出
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             mac_data_sop <= ;
         )
             mac_data_sop <= ;
         else
             mac_data_sop <= ;
     end

     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             mac_data_eop <= ;
         else if(end_normal || end_lag)
             mac_data_eop <= ;
         else
             mac_data_eop <= ;
     end

     :]      > 'd1;
     :] <= 'd1;

     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             mac_data <= ;
         else if(add_head_cnt)//由于MAC不是32位数据的整数倍,需要对数据进行移位
             mac_data <= mac_head[ - head_cnt* -: ];
         else if(head_neg)
             mac_data <= {mac_head[:],q[:]};
         else
             mac_data <= data_shift;
     end

     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             mac_data_vld <= ;
         else if(head_flag || rd_flag || end_lag)
             mac_data_vld <= ;
         else
             mac_data_vld <= ;
     end

     //输出无效字节个数 由于输出端进行了数据移位,导致无效数据个数发生变化
     always@(posedge clk or negedge rst_n)begin
         if(!rst_n)
             mac_data_mod <= ;
         else if(end_normal)
             mac_data_mod <= q[:] - ;
         :] == 'd1)
             mac_data_mod <= ;
         :] == )
             mac_data_mod <= ;
         else
             mac_data_mod <= ;
     end

 endmodule

编写测试激励验证功能:

 `timescale 1ns / 1ps

 module add_mac_head_tb;

     reg clk,rst_n;
     :] app_data;
     reg app_data_sop,app_data_eop,app_data_vld;
     :] app_data_mod;
     reg mac_tx_rdy;

     :] mac_data;
     wire mac_data_vld,mac_data_sop,mac_data_eop;
     :] mac_data_mod;

     add_mac_head add_mac_head(
     .clk(clk),
     .rst_n(rst_n),
     .app_data(app_data),
     .app_data_vld(app_data_vld),
     .app_data_sop(app_data_sop),
     .app_data_eop(app_data_eop),
     .app_data_mod(app_data_mod),//无效字节数

     .mac_tx_rdy(mac_tx_rdy),//MAC IP发送准备就绪信号
     .mac_data(mac_data),
     .mac_data_vld(mac_data_vld),
     .mac_data_sop(mac_data_sop),
     .mac_data_eop(mac_data_eop),
     .mac_data_mod(mac_data_mod)
     );

     ,
               RST_TIME = ;

     integer i;

     initial begin
         clk = ;
         forever #(CYC / 2.0) clk = ~clk;
     end

     initial begin
         rst_n = ;
         #;
         rst_n = ;
         #(CYC*RST_TIME);
         rst_n = ;
     end

     initial begin
         #;
         app_data = ;
         app_data_sop = ;
         app_data_eop = ;
         app_data_mod = ;
         app_data_vld = ;
         mac_tx_rdy = ;
         #(CYC*RST_TIME);
         packet_gen(,);
         packet_gen(,);
         packet_gen(,);
         #;
         $stop;
     end

     task packet_gen;
         :] length;
         :] invld_num;
         begin
             app_data_vld = ;
             app_data_sop = ;
             app_data = 'h01020300;
             ;i < length;i = i + 'b1)begin
                 )
                     app_data_sop = ;
                 )begin
                     app_data_mod = invld_num;
                     app_data_eop = ;
                 end
                 app_data = app_data +'b1;
                 #(CYC*);
             end
             app_data_eop = ;
             app_data_vld = ;
             app_data_mod = ;
         end
     endtask

 endmodule

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

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

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

IP核之初——FIFO添加以太网MAC头部的更多相关文章

  1. Xilinx IP核使用(一)--FIFO

    今天在将SRIO的数据存入FIFO后,然后把FIFO中的数据不断送入FFT进行运算时,对于几个控制信号总产生问题.所以单独对FIFO进行了仿真.原来感觉FIFO的几个参数端口一目了然啊,还需要什么深入 ...

  2. FIFO IP核仿真

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

  3. 如何用ModelsimSE仿真IP核-以PLL为例

    我们之前介绍了如何使用Modelsim SE进行仿真和利用do文件的仿真方法,但是其中待仿真的模块是我们自己编写的Verilog模块,但是在实际工作中,我们的设计中会经常用到FPGA厂商给我们提供的现 ...

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

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

  5. FIFO IP核

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

  6. Altera三速以太网IP核快速仿真与使用(上篇)

    对于比较高级的ip核,altera一般都会提供仿真案例,网上有关于这个IP核的各种仿真方法,但都比较繁琐,前几日,朋友跟我分享了一个比较快速高效的仿真方法,这个方法也是他摸索折腾了一段时间才总结出来的 ...

  7. Altera FIFO IP核时序说明

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

  8. Altera三速以太网IP核使用(下篇)--- 百兆网接口设计与使用

    MAC IP核的主要作用是:实现数据链路层协议,分为TX方向与RX方向,TX方向实现的是在原包文的前面加上7个55和1个D5,RX方向则相反.在使用这个 MAC IP核之前,首先确认下自己使用的网卡是 ...

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

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

随机推荐

  1. Eclipse rap 富客户端开发总结(5): RAP国际化之路

    Eclipse RCP 中的plugin.xml国际化实现 1.  在工程的根目录下面建立一个plugin.properties资源文件:在此资源文件中写入需要国际化的内容(键/值对),举例如下: h ...

  2. wampserver启动不起来的原因?

    如果没怎么动wamp的配置文件就发现wampserver启动不起来了,那么可能你碰到了iis服务器. 原因是apache的端口占用的是80,而iis的端口占用也是80所以造成了不能启动wampserv ...

  3. Failed to load the JNI shared library "XXXXXXX"

    今天启动Eclipse的时候出现了这个问题,经过查找, 一般来说这种问题都是因为eclipse 和Java 的兼容性不一致所导致的. 1) 查看Eclipse 和Java 版本 那么我们需要分别查看下 ...

  4. ubuntu下pip的安装和使用

    对于python包的安装而言,需要pip包,对python包资源管理.pip包的安装.对于python2.x和python 3.x 方法不同 : Python 2: sudo dnf upgrade ...

  5. oracle 表查询(二)

    1.使用逻辑操作符号问题:查询工资高于500或者是岗位为manager的雇员,同时还要满足他们的姓名首字母为大写的J?select * from emp where (sal > 500 or ...

  6. websocket的理解及实例应用

    websocket协议是HTML5提出的一个新的规范,主要用于实现服务器及时推送信息给客户端的功能. websocket实现是基于HTTP协议的部分握手功能,但是websocket仅仅握手一次即可进行 ...

  7. 一个基于Asp.net MVC的博客类网站开源了!

    背景说明: 大学时毕业设计作品,一直闲置在硬盘了,倒想着不如开源出来,也许会对一些人有帮助呢,而且个人觉得这个网站做得还是不错了,毕竟是花了不少心思,希望对你有所帮助. github地址:https: ...

  8. HDFS概述(5)————HDFS HA

    HA With QJM 目标 本指南概述了HDFS高可用性(HA)功能以及如何使用Quorum Journal Manager(QJM)功能配置和管理HA HDFS集群. 本文档假设读者对HDFS集群 ...

  9. JSON依赖的选择

    json-lib 源码:https://github.com/aalmiray/Json-lib/ 最新版本:2.4 不再更新 <dependency> <groupId>ne ...

  10. poj2528 线段树+离散化 (倒序)

    The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign h ...