葡京网上娱乐场IP核之初——FIFO添加以太网MAC头部

 

ARC的内存管理

来探同样段落ARC条件下之代码

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

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

简短的话即使是ARC以代码编译阶段,会自行在代码的上下文中变成对插入retain以及release,保证引用计数能够正确管理内存。如果目标不是赛引用类型,那么ARC的拍卖啊会进展对应的更动

下会分别证实当马上几只同援计数相关的艺术调用中来了呀

  其中sop和eop分别是包头,包尾指示信号,data_vld是数量中指示信号。由于数量位富有此处是32各类,而数据的最小单元是字节,所以每个32个数据不必然带有4个字节有效数据,使用data_mod指示出无效字节数。为了吃该模块输出端知道何时输出了一个数据包,要把eop信号及数据信号拼接写副FIFO中,这样输出端发出eop时上新一轱辘循环。如果根据写副sop信号来当开发送MAC头部和数据有的表明,试想当一个短包紧跟着一个抬高包写上FIFO中时,输出端正在送出落得一样长管剩下的几乎单数据,无法响应短包的sop信号指示,那么短包即让“丢弃”了。为了避免丢包现象,需要满足“读写隔离规则”,即FIFO读操作以及描绘操作两者不能够依据同样着的情事来控制另外一样正在的行事。进一步引出“双FIFO架构”,使用数据FIFO缓存数据,而信息FIFO保留指示信息,这样说写侧的指令信号写副信息FIFO中,数据FIFO可以因信息FIFO读侧的音信来判断读之行事,也不怕满足了读写隔离规则。

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各队不但减少了额外占用的标记变量内存,还能够坐当援计数是否归零的论断

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

前言

ARC用作一个外常谈的话题,基本让网上的各种博客说老了。但是前段时间朋友通过某些手段对YYModel拓展了优化,提高了约1/3左右的频率,在欣赏了他改进之源码之后我以更看了平等任何ARC有关的实现源码,主要反映ARC建制的几乎独艺术分别是retainrelease以及dealloc,主要与strongweak两者相关

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

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差一点千篇一律,就不再介绍了

  开始编制代码了:

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;
}

旁一个弱引用设置方式,相比上一个措施去丢了自旋锁加锁操作

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

dealloc

dealloc是重量级的点子之一,不过是因为函数内部调整用层次过多,这里不多阐述。实现代码在objc-object.h798尽,可以自行到官网下充斥源码后研读

 

尾话

先是使肯定,相比起其它属性恶鬼改进之优化,使用__unsafe_unretained带动的获益几乎凤毛麟角,因此笔者并无是挺推荐用这种高资本没有回报的章程优化项目,起码在性能恶鬼大头解决前未推荐,但是去学习内存管理根的知可以辅助我们站于还胜似之地方对开发。

ps:在爱人的坚持下,可耻的撤了代码链接

上一篇:信机制
下一篇:分拣为何不生成setter和getter

转载请注明本文作者及地点

葡京网上娱乐场 1

__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修饰对象,那么这部分的调用损耗就受节省下来,这为是作者朋友改进之手法

 编写测试激励验证功能:

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

  第二页中需要特别强调的凡读模式的选。其实这里的First
Word Fall Through对应的便是Altera FPGA中FIFO IP核读模式面临的Show
ahead模式嘛,换个名而已。这个读模式的表征是以宣读而能立竿见影之前,即把FIFO中第一只数据由读数据端口不断送出。在这种模式下,读而能信号倒像是“读清”信号,把上同一糟糕的数码清除掉,让FIFO送出下一个数。这样做的介乎是称dout
和dout_vld相匹配的输出信号方式。

 

葡京网上娱乐场 2

葡京网上娱乐场 3

 

 

葡京网上娱乐场 4

  第四页可选FIFO内缓存数据量计数器,由于自家起来选的凡异步FIFO模式,所以是处来少单计数器分别和读侧和写侧时钟上升沿同。注意一点:这有限个计数器均代表FIFO缓存数据量,只不过在钟上稍加病,切不可错误理解吧是写副了或者读来了略微个数据。

  有矣色求,设计思路后显著模块接口列表:

 

葡京网上娱乐场 5

 

 

葡京网上娱乐场 6

  4)
知道其中寄存器地址和作用以及配置方式、顺序;

  第三页是布置有可是摘的标志位,可以因需要巧实现有标志位和抓手特性(我是从来没有因此了)。

 

  我觉得其中最为着重之几触及如下:

   连续输入三单长不同之报文,此处为筹重用,用而参数化的task对鼓舞报文进行包装。需要输入报文时独自待调用packet_gen任务即可兑现所有不同长度,不同无效字节个数的数据包。观察输出波形:

 

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

葡京网上娱乐场 7

  配置向导中率先页中是选取FIFO的接口模式与促成方式。这里我们用旧之接口方式。箭头处是贯彻方式,如果欲异步时钟域处理选择读写独立钟模式。

葡京网上娱乐场 8

 

 

  MAC头部信息也14字节,而数位宽是32各,即一律潮发送四独字节,所以一定给头部也老三单半数码。因此于发送第三只头部数时,低16各项而就此数码有填充,后止的数也只要接着移动。如此走操作后,数据有即使后了平碰输出最后16各类。如果最后就16各项数据遭到发出立竿见影字节,那么mac_data_vld当前节奏也要使得,且mac_data_eop和mac_data_mod跟着晚同碰撞输出;如果听由有效字节,则依照正常状况输出。在代码中本身利用end_normal和end_lag信号来分别上述两栽状况。需要特别注意的凡,在移动操作后数包中包含的失效字节个数也会发生变化。为了理清思路和时序,画来中心信号时先后图:

  今天来讲说一个顶常用之IP核,FIFO。可以说其是FPGA能这样灵活处理数据的根基,常用于异步时钟域处理、位富有转换和需要数缓存的场合。先来说明下,对于新家与刚刚沾一个IP核的人口吧,不要过于关注IP核的各级一个参数和效能,更没有必要知道其中的有血有肉组织以及工作原理(还尚未忘记之前使用的ILA吧,反正自己是匪知道具体怎么统筹出来的)只需要控制最常用之跟无限要害的,把IP核用起来便坏功告成了。先从生成IP核开始吧:

 

葡京网上娱乐场 9

 

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

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

 

葡京网上娱乐场 10

 

   

 

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

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

葡京网上娱乐场 11

 

 

葡京网上娱乐场 12

葡京网上娱乐场 13

 

 

 

葡京网上娱乐场 14

 

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

 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

  最后总结页,把前面的参数和部署汇总下。没有问题得以点击OK了!

 

 葡京网上娱乐场 15

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

 

  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