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

  最后总括页,把前面的参数和布局汇总下。没有问题能够点击OK了!

前言

ARC作为一个老生常谈的话题,基本被网上的各样博客说尽了。不过前段时间朋友通过一些手段对YYModel进展了优化,提升了大约1/3左右的功能,在欣赏过她立异的源码之后我又重新看了两次ARC相关的兑现源码,重要反映ARC编制的几个模式分别是retainrelease以及dealloc,主要与strongweak两者相关

 图片 1

release

release调用有着跟retain接近的三回调用,前两回调用的效应一样,因而这里只放上引用计数收缩的函数代码:

uintptr_t objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.indexed);
#endif
    SideTable& table = SideTables()[this];

    bool do_dealloc = false;

    if (table.trylock()) {
        RefcountMap::iterator it = table.refcnts.find(this);
        if (it == table.refcnts.end()) {
            do_dealloc = true;
            table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
        } else if (it->second < SIDE_TABLE_DEALLOCATING) {
            // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
            do_dealloc = true;
            it->second |= SIDE_TABLE_DEALLOCATING;
        } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
            it->second -= SIDE_TABLE_RC_ONE;
        }
        table.unlock();
        if (do_dealloc  &&  performDealloc) {
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return do_dealloc;
    }

    return sidetable_release_slow(table, performDealloc);
}

release中决定对象是否会被dealloc有多个第一的判定

  • 万一引用计数为计数表中的最后一个,标记对象为正在析构处境,然后实施到位后发送SEL_dealloc音讯放出对象
  • 不畏计数表的值为零,sidetable_retainCount函数照样会回来1的值。这时计数小于宏定义SIDE_TABLE_DEALLOCATING == 1,就不进行压缩计数的操作,直接标记对象正在析构

看到release的代码就会意识在地方代码中宏定义SIDE_TABLE_DEALLOCATING呈现出了苹果那么些心机婊的苦读之深。平日而言,虽然引用计数唯有8位的占有,在剔除了第一位越界标记以及后两位后,其最大取值为2^5-1 == 31位。经常来说,假使不是项目中block不加限制的引用,是很难达标如此多的引用量的。由此占有了SIDE_TABLE_DEALLOCATING位不但缩小了额外占用的标记变量内存,仍能以作为引用计数是否归零的论断

 

weak

最开端的时候没打算讲weak本条修饰,不过因为dealloc办法本身涉及到了弱引用对象置空的操作,以及retain过程中的对象也跟weak有涉嫌的意况下,总而言之说weak的操作

bool objc_object::sidetable_isWeaklyReferenced()
{
    bool result = false;

    SideTable& table = SideTables()[this];
    table.lock();

    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        result = it->second & SIDE_TABLE_WEAKLY_REFERENCED;
    }

    table.unlock();

    return result;
}

weakstrong共用一套引用计数设计,因而双方的赋值操作都要安装计数表,只是weak修饰的目标的引用计数对象会被设置SIDE_TABLE_WEAKLY_REFERENCED位,并且不加入sidetable_retainCount函数中的计数总计而已

void objc_object::sidetable_setWeaklyReferenced_nolock()
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.indexed);
#endif

    SideTable& table = SideTables()[this];

    table.refcnts[this] |= SIDE_TABLE_WEAKLY_REFERENCED;
}

另一个弱引用设置情势,相比上一个艺术去掉了自旋锁加锁操作

  第二页中需要特别强调的是读形式的取舍。其实这里的First
Word Fall Through对应的就是Altera FPGA中FIFO IP核读情势中的Show
ahead形式嘛,换个名字而已。这些读形式的特点是在读使能有效在此之前,即把FIFO中第一个数据从读数据端口不断送出。在这种情势下,读使能信号倒像是“读清”信号,把上两次的多少清除掉,让FIFO送出下一个数量。这样做的处是顺应dout
和dout_vld相包容的出口信号模式。

尾话

第一要肯定,相比较起其余性能恶鬼改进的优化,使用__unsafe_unretained带来的入账几乎凤毛麟角,因而笔者并不是很推荐用这种高成本低回报的点子优化品种,起码在性能恶鬼大头解决此前不推荐,然则去学学内存管理底层的文化可以帮忙咱们站在更高的地方看待开发。

ps:在爱人的坚韧不拔下,可耻的撤除了代码链接

上一篇:信息机制
下一篇:分类为啥不生成setter和getter

转载请申明本文作者和地点

图片 2

retain

强引用有retainstrong以及__strong几种修饰,默认处境下,所有的类对象会自行被标识为__strong强引用对象,强引用对象会在上下文插入retain以及release调用,从runtime源码处可以下载到相应调用的源代码。在retain调用的历程中,总共涉及到了四回调用:

  • id _objc_rootRetain(id obj)
    对传播对象举办非空断言,然后调用对象的rootRetain()方法
  • id objc_object::rootRetain()
    断言非GC条件,倘使目的是TaggedPointer指南针,不做处理。TaggedPointer是苹果推出的一套优化方案,具体可以参见深深领会Tagged
    Pointer
    一文
  • id objc_object::sidetable_retain()
    充实引用计数,具体往下看
  • id objc_object::sidetable_retain_slow(SideTable& table)
    追加引用计数,具体往下看

在下面的几步中最着重的手续就是最终两部的充实引用计数,在NSObject.mm中可以见见函数的贯彻。这里笔者剔除了一部分不相干的代码:

#define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
#define SIDE_TABLE_DEALLOCATING      (1UL<<1)
#define SIDE_TABLE_RC_ONE            (1UL<<2)
#define SIDE_TABLE_RC_PINNED         (1UL<<(WORD_BITS-1))

typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;
}

id objc_object::sidetable_retain()
{
    // 获取对象的table对象
    SideTable& table = SideTables()[this];

    if (table.trylock()) {

        // 获取 引用计数的引用
        size_t& refcntStorage = table.refcnts[this];
        if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
            // 如果引用计数未越界,则引用计数增加
            refcntStorage += SIDE_TABLE_RC_ONE;
        }
        table.unlock();
        return (id)this;
    }
    return sidetable_retain_slow(table);
}
  • SideTable这一个类富含着一个自旋锁slock来防护操作时或许出现的多线程读取问题、一个弱引用表weak_table以及引用计数表refcnts。其它还提供一个办法传入对象地址来搜寻对应的SideTable对象

  • RefcountMap对象通过散列表的协会存储了目标持有者的地点以及引用计数,这样一来,虽然目的对应的内存出现错误,例如Zombie不行,也能稳定到目的的地方音讯

  • 每次retain后从此引用计数的值实际上扩展了(1 << 2) == 4而不是大家所知的1,这是出于引用计数的后两位分别被弱引用以及析构状态多少个标识位占领,而首先位用来表示计数是否越界。

鉴于引用计数可能存在越界情状(SIDE_TABLE_RC_PINNED位的值为1),由此散列表refcnts中应有储存了四个引用计数,sidetable_retainCount()函数也表明了这或多或少:

#define SIDE_TABLE_RC_SHIFT 2
uintptr_t objc_object::sidetable_retainCount()
{
    SideTable& table = SideTables()[this];
    size_t refcnt_result = 1;

    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
    }
    table.unlock();
    return refcnt_result;
}

引用计数总是回到1 + 计数表总计本条数值,这也是为啥平常性的当对象被释放后,我们得到retainCount的值总不可以为0。至于函数sidetable_retain_slow的实现和sidetable_retain差一点一样,就不再介绍了

 

__unsafe_unretained

其实写了这么多,终于把本文的中流砥柱给讲出来了。在iOS5的时候,苹果正式推出了ARC体制,伴随的是地方的weakstrong等新修饰符,当然还有一个不常用的__unsafe_unretained

  • weak
    修饰的靶子在针对的内存被放飞后会被电动置为nil
  • strong
    具有指向的目标,会让引用计数+1
  • __unsafe_unretained
    不引用指向的对象。但在对象内存被放走掉后,如故指向内存地址,等同于assign,然则只可以修饰对象

在机械上确保应用能维持在55帧以上的速率会让使用看起来如棉布般顺滑,不过稍有不慎,稍微降到50~55中间都有很大的或许彰显出卡顿的气象。这里不谈及图像渲染、数据大量拍卖等耳闻能详的属性恶鬼,说说Model所造成的损耗。

如前方所说的,在ARC条件下,对象的默认修饰为strong,这意味着这么一段代码:

@protocol RegExpCheck

@property (nonatomic, copy) NSString * regExp;

- (BOOL)validRegExp;

@end

- (BOOL)valid: (NSArray<id<RegExpCheck>> *)params {
    for (id<RegExpCheck> item in params) {
        if (![item validRegExp]) { return NO; }
    }
    return YES;
}

把这段代码改为编译期间插入retainrelease方法后的代码如下:

- (BOOL)valid: (NSArray<id<RegExpCheck>> *)params {
    for (id<RegExpCheck> item in params) {
        [item retain];
        if (![item validRegExp]) { 
            [item release];
            return NO;
        }
        [item release];
    }
    return YES;
}

遍历操作在类型中冒出的概率相对排的上前列,那么地方这么些艺术在调用期间会调用params.countretainrelease函数。常常来说,每一个对象的遍历次数越多,这多少个函数调用的耗费就越大。假如换做__unsafe_unretained修饰对象,那么这有些的调用损耗就被节省下来,这也是笔者朋友革新的手腕

  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

dealloc

dealloc是重量级的主意之一,可是出于函数内部调用层次过多,这里不多解说。实现代码在objc-object.h798行,可以活动到官网下载源码后研读

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

ARC的内存管理

来看望一段ARC环境下的代码

  • (void)viewDidLoad {
    NSArray * titles = @[@”title1″, @”title2″];
    }
    在编译期间,代码就会化为这样:

    • (void)viewDidLoad {
      NSArray * titles = @[@”title1″, @”title2″];
      [titles retain];
      /// …….
      [titles release];
      }

简言之的话就是ARC在代码编译阶段,会自动在代码的上下文中成对插入retain以及release,保证引用计数可以正确管理内存。即使目标不是强引用类型,那么ARC的处理也会进展对应的转移

下边会独家证实在那多少个与引用计数相关的法门调用中暴发了什么样

图片 3

 

  起头编制代码了:

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

 

  第四页可选FIFO内缓存数据量计数器,由于我起来采用的是异步FIFO形式,所以此处有六个计数器分别与读侧和写侧时钟上升沿同步。注意一点:那七个计数器均表示FIFO缓存数据量,只不过在时钟上有些偏差,切不可错误明白为是写入了或者读出了多少个数据。

 编写测试激励验证功用:

 

 

 

 

  4)
知道其中寄存器地址及功效和配置模式、顺序;

图片 4

图片 5

 

 

  其中sop和eop分别是莆田,包尾提示信号,data_vld是数量有效提示信号。由于数量位宽此处是32位,而数据的细单反元是字节,所以每个32位数据不肯定带有4个字节有效数据,使用data_mod提示出无效字节数。为了让该模块输出端知道哪天输出完一个数据包,要把eop信号和数据信号拼接写入FIFO中,这样输出端发出eop时进入新一轮循环。假使按照写入sop信号来作为开首发送MAC头部和数目部分的标志,试想当一个短包紧跟着一个长包写进FIFO中时,输出端正在送出上一长包剩下的多少个数据,不可能响应短包的sop信号指示,那么短包即被“舍弃”了。为了避免丢包现象,需要知足“读写隔离规则”,即FIFO读操作和写操作两者无法依据一方的情景来支配另一方的作为。进一步引出“双FIFO架构”,使用数据FIFO缓存数据,而音信FIFO保留指示新闻,这样讲写侧的指示信号写入音信FIFO中,数据FIFO可以根据音讯FIFO读侧的音信来判断读的所作所为,也就满足了读写隔离规则。

图片 6

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

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

 

 

 

 

图片 7

 

 

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

 

图片 8

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

 

 

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

 

 

图片 9

图片 10

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

  2) 明确各样紧要用户接口效率;

 

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

  第三页是布置部分可选的标志位,可以按照需要灵活实现部分标志位和抓手特性(我是一贯没用过)。

图片 11

 

 

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

  MAC头部信息为14字节,而数据位宽是32位,即一遍发送六个字节,所以一定于头部为六个半数据。因而在发送第七个头部数据时,低16位要用数据部分填充,前面的多少也要跟着移动。如此移位操作后,数据部分就晚了一拍输出最终16位。假设最后这16位数据中有管用字节,那么mac_data_vld当前节奏也要管用,且mac_data_eop和mac_data_mod跟着晚一拍输出;假设无有效字节,则遵照正规情况输出。在代码中我利用end_normal和end_lag信号来区别上述二种情形。需要特别注意的是,在活动操作后数据包中包含的不算字节个数也会暴发变化。为了理清思绪和时序,画出主题信号时序图:

  3) 了解所需指令的操作时序;

图片 12

  配置向导中首先页中是采纳FIFO的接口模式和兑现格局。这里我们用原始的接口情势。箭头处是贯彻格局,假使需要异步时钟域处理接纳读写独登时钟情势。

  我觉着其中最着重的几点如下:

   

  后日来讲讲一个最常用的IP核,FIFO。可以说它是FPGA能如此灵活处理数据的功底,常用来异步时钟域处理、位宽转换以及需要多少缓存的场合。先来表达下,对于初学者和刚接触一个IP核的人的话,不要过于关注IP核的每一个参数和职能,更没必要明白其中的切切实实社团和劳作规律(还没忘在此以前运用的ILA吧,反正我是不知情具体怎么规划出来的)只需通晓最常用的和最着重的,把IP核用起来就大功告成了。先从生成IP核起头吧:

 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

图片 13

图片 14

   连续输入五个长度不同的报文,此处为了设计重用,用可参数化的task对鼓舞报文举行包装。需要输入报文时只需调用packet_gen任务即可兑现所有不同长短,不同无效字节个数的数据包。观察输出波形:

 

图片 15