• OPNET学习笔记之defer模块(ethcoax_station_adv节点模型)


    defer模块的进程模型是eth_defer_v2,功能是"Deference:For CSMA/CD Ethernet, the process by which a station delays its transmission when the channel is busy to avoid contention with ongoing transmission",就是检测链路的情况,并保持一个延迟标志(deference flag),使mac模块能通过统计线读取来确定传输是否被允许,以避免冲突。

    该模块没有model attribute、global statistics和global attributes,有两个local statistics,分别为deference time和deference variable,注意1,process interface中beginsim interrput为不触发。2,该模块没有包流,只有3条统计线,如下:

    statwire : defer.Ethcoax.Deference Variable -> mac.instat [0]  //通知mac模块         falling enabled       

    statwire : mac.Ethcoax.Frame Waiting -> defer.instat [0]    //来自mac,是否有帧在等待    falling enabled
    statwire : bus_rx0.channel [0].bus receiver.busy -> defer.instat [1]  //来自rcv,收信机是否忙  rising/falling enabled

     

    状态机如下:

    由于beginsim interrput为不触发,所以INIT的触发条件???

    一,header block中定义的全局宏定义:

    /* Output statistic wires */
    #define DEFERENCE_OUTSTAT 0  //到mac模块的统计线,表示deference flag

    /* Input statistic wires */
    #define FRAME_WAITING_INSTAT 0  //从mac模块接收的统计线,表示是否有frame等待,到instat[0]
    #define CARRIER_SENSE_INSTAT 1  //从接收器模块的统计线,表示信道是否忙,到instat[1]

    /* Interframe spacing set to 9.6 microseconds. */  //帧间隔???或退避时间???
    #define INTERFRAME_SPACING 9.6E-06

    /* Macros for state transitions */
    #define FRAME_WAITING (frame_waiting != 0)  //有frame等待,临时变量,结果从函数eth_defer_frame_waiting()得到
    #define FRAME_WAITING_LOW (intrpt_stat_index == FRAME_WAITING_INSTAT && !FRAME_WAITING)  //当前无frame等待,且产生统计量中断源为FRAME_WAITING_INSTAT,注意这个中断只有在输入统计量变低时产生,表示没有frame等待???(或frame发送完成???)

    #define CHANNEL_BUSY (carrier_sense != 0)  //信道状态为忙,通过eth_defer_carrier_sense()函数查询得到
    #define BUSY_HIGH (intrpt_stat_index == CARRIER_SENSE_INSTAT && CHANNEL_BUSY)//当前信道忙,产生统计量中断源为rx0(含义为信道变忙???)
    #define BUSY_LOW (intrpt_stat_index == CARRIER_SENSE_INSTAT && !CHANNEL_BUSY)//当前信道空闲,(含义为信道变空闲???)

    #define SPACING_ELAPSED (op_intrpt_type () == OPC_INTRPT_SELF)  //中断源为自中断
    #define ENABLE_INTRPTS (op_intrpt_enable_all ())        //使能所有中断

     

    二,INIT状态的入口代码:

    /* Initialize variables and register statistics. */  //都是state variable
    def_on_time = 0.0;
    def_off_time = 0.0;
    total_deference_time = 0.0;

    /* Declare statistics handles. */  //注册局部统计量,返回句柄
    local_deference_handle = op_stat_reg ("Ethcoax.Deference Time (sec)", OPC_STAT_INDEX_NONE, OPC_STAT_LOCAL);
    def_handle = op_stat_reg ("Ethcoax.Deference Variable", OPC_STAT_INDEX_NONE, OPC_STAT_LOCAL);  //注意这个统计量是统计线的源

    该状态是强制状态,完成后会直接跳转到DIFF_OFF状态并执行入口代码:

    三,分析其function block:
    static int eth_defer_carrier_sense ()  //读取bus_rx0模块的统计量,测试介质是否空闲,返回值为bool型,1:buzy,0:idle
    {
    double stat_val;

    /** Return 1 if there is traffic on the bus, zero if not. **/
    FIN (eth_defer_carrier_sense ());

    /* Read the carrier status. Check for errors. */
    stat_val = op_stat_local_read (CARRIER_SENSE_INSTAT);  //读取统计线的值,为bus_rx0的统计量buzy,是一个bool型(1/0)
    if (stat_val == OPC_DBL_INVALID)
    {
    eth_defer_error ("Unable to read carrier sense statistic.", OPC_NIL, OPC_NIL);  //读取统计线数据错误
    }

    if (stat_val == 1.0)  //buzy,返回1
    {
    FRET (1);    //注意函数的返回值形式
    }
    else
    {
    FRET (0);
    }
    }

    static int eth_defer_frame_waiting ()  //和上一个函数类似,读取mac模块的统计量frame waiting,测试是否有帧需要发送,1:waiting,0:idle
    {
    double stat_val;

    /** Return 1 if a frame is waiting to be sent, zero if not. **/
    FIN (eth_defer_frame_waiting ());

    /* Read the frame waiting statistic. Check for errors. */
    stat_val = op_stat_local_read (FRAME_WAITING_INSTAT);  //注意在header block中定义的宏,为统计线instat[0]
    if (stat_val == OPC_DBL_INVALID)
    {
    eth_defer_error ("Unable to read frame waiting statistic.", OPC_NIL, OPC_NIL);
    }

    if (stat_val == 1.0)
    {
    FRET (1);
    }
    else
    {
    FRET (0);
    }
    }

    static void eth_defer_error (const char* msg0, const char* msg1, const char* msg2)  //这两个函数都是进行错误处理,给出错误信息并结束仿真
    {
    /** Print an error message and exit the simulation. **/
    FIN (eth_defer_error (msg0, msg1, msg2));

    op_sim_end ("Error in Ethernet deference model (eth_defer):", msg0, msg1, msg2);

    FOUT;
    }


    static void eth_defer_warn (const char* msg0, const char* msg1, const char* msg2)

    {
    /** Print a warning message and exit the simulation. **/
    FIN (eth_defer_warn (msg0, msg1, msg2));

    op_prg_odb_print_major ("Warning from Ethernet deference model (eth_defer):",
    msg0, msg1, msg2, OPC_NIL);

    FOUT;

    }

    四,DEF_OFF状态是强制状态,首先执行入口代码:

    /* Test for waiting frames and busy carrier. */  //测试介质是否忙及是否有frame需要发送
    carrier_sense = eth_defer_carrier_sense ();    //(0:idle,1:busy)
    frame_waiting = eth_defer_frame_waiting ();

    /* If a frame is waiting and the carrier is not busy, turn deference off.*/  //介质空闲,或者有frame需要发送,则写0到统计量,允许发送???
    if (frame_waiting || !carrier_sense)  //这里感觉应该是与的关系,怎么是或呢???
    {
    def_off_time = op_sim_time();      //开始计算off的时间
    deference_time = def_off_time - def_on_time;  //def_on_time开始时为0
    total_deference_time += deference_time;
    op_stat_write (local_deference_handle, total_deference_time); 
    op_stat_write (def_handle, 0.0);    //写统计量,def_handle为0,注意到mac模块的统计线的触发器为低触发,会将deference flag置0。
    }

    然后根据条件跳转:

    (一)FRAME_WAITING:当存在帧需要传递,到FRAME_WAIT状态,等待帧发送完成。

    //定义在header block中:#define FRAME_WAITING(frame_waiting != 0)

    由于在上一状态中deference是低,则mac模块可以发送帧,当发送完成后,发给defer模块统计量中断???

    等待统计量中断,然后执行其出口函数(一直等到帧发送完):

    /* Make sure that this is a statistic interrupt. */  //首先判断是否是统计量中断(只允许这种中断),这时候有两种中断,一是来自mac的frame waiting变低,表示帧发送完成;二是来自rcv的busy变化,表示碰撞???但是本状态会一直等到帧发送完后,判断rcv状态再跳转,否则等待。
    if (op_intrpt_type () != OPC_INTRPT_STAT)
    {
    eth_defer_error ("Received unexpected interrupt type in state FRM_WAIT.",
    "Only statistic interrupts are allowed in this state.",OPC_NIL);
    }
    intrpt_stat_index = op_intrpt_stat ();    //得到中断统计量的index,以操作之

    carrier_sense = eth_defer_carrier_sense ();  //更新temp variable
    frame_waiting = eth_defer_frame_waiting ();

    然后判断转换条件:

    <1>如果是FRAME_WAITING_LOW && !CHANNEL_BUSY,

      #define FRAME_WAITING_LOW(intrpt_stat_index == FRAME_WAITING_INSTAT && !FRAME_WAITING)

    即mac中没有帧等待发送了,且信道空闲;这时跳转到BSY_WAIT状态,注意

    等待中断,期待的这个中断是BUZY_HIGH,即信道变忙了

    /* Make sure that this is a statistic interrupt. */    //首先判断是否是统计量中断(只允许这种中断)
    if (op_intrpt_type () != OPC_INTRPT_STAT)
    {
    eth_defer_error ("Received unexpected interrupt type in state BSY_WAIT.",
    "Only statistic interrupts are allowed in this state.",OPC_NIL);
    }
    intrpt_stat_index = op_intrpt_stat ();  //得到中断统计量的index

    carrier_sense = eth_defer_carrier_sense ();

    然后跳转到DEF_ON状态。

    <2>如果是FRAME_WAITING_LOW && CHANNEL_BUSY,

    即mac中没有帧等待发送了,且信道忙;这时跳转到DEF_ON状态,

    <3>如果是其它(只可能是从bus_rx0来的,这时mac模块的帧发送未完成),保持原状态。

    (2)!FRAME_WAITING && !CHANNEL_BUSY:没有帧等待且信道空闲,进入BUZY_WAIT状态。

    (3)default:其它,没有帧等待且信道忙,进入DEF_ON状态。

    (二)!FRAME_WAITING&&!CHANNEL_BUSY,没有帧等待且信道空闲,直接进入BUZY_WAIT状态。

    (三)default,表示没有帧等待且信道忙,进入DEF_ON状态。

    五,DEF_ON状态,执行入口代码:

    /* Channel just became busy. Turn on deference. */  //更新def_on_time,使deference flag为1
    def_on_time = op_sim_time();
    op_stat_write (def_handle, 1.0);

    由于DEF_ON状态是强制状态,马上跳转到FREE_WAIT状态,等待中断

    六,FREE_WAIT状态

    由于deference flag为高,这时mac模块应不能发送,期待的中断应该是信道空闲,当BUZY_LOW条件满足时,执行出口代码:

    /* Make sure that this is a statistic interrupt. */  //只接收统计量中断
    if (op_intrpt_type () != OPC_INTRPT_STAT)
    {
    eth_defer_error ("Received unexpected interrupt type in state FREE_WAIT.",
    "Only statistic interrupts are allowed in this state.",OPC_NIL);
    }
    intrpt_stat_index = op_intrpt_stat ();

    carrier_sense = eth_defer_carrier_sense ();

    然后跳转到SPACING状态。

    七,SPACING状态,首先执行入口代码:

    /* Schedule an interrupt to mark */
    /* the end of the interframe gap. */
    evh = op_intrpt_schedule_self (op_sim_time () + INTERFRAME_SPACING, 0);  //自中断,等待帧间隔时间唤醒自己
    if (op_ev_valid (evh) == OPC_FALSE)
    {
    eth_defer_error ("State SPACING: Unable to schedule end of interframe gap.",
    OPC_NIL, OPC_NIL);
    }

    /* During the interframe gap, ignore all statistic wire interrupts. */  //在此期间忽略一切中断
    op_intrpt_disable (OPC_INTRPT_STAT, FRAME_WAITING_INSTAT, OPC_FALSE);
    op_intrpt_disable (OPC_INTRPT_STAT, CARRIER_SENSE_INSTAT, OPC_FALSE);

    当自中断完成后,开启中断:

    #define SPACING_ELAPSED (op_intrpt_type () == OPC_INTRPT_SELF)
    #define ENABLE_INTRPTS (op_intrpt_enable_all ())

    然后回到def_off状态。

    总结:

  • 相关阅读:
    深入理解Node.js垃圾回收与内存管理
    【File System】Node.js中文件操作模块File System
    【事件流】事件冒泡和事件捕获
    undefined 和null的区别?
    localStorage实现登录注册功能
    解刨for循环
    react中嵌入高德地图并Marker标点
    react页面中嵌入地图,标识出某个地点,使用插件react-amap
    react中使用antd的List组件,以及下载文件,List隔行变色
    公众号页面数据处理
  • 原文地址:https://www.cnblogs.com/loopever/p/2601653.html
Copyright © 2020-2023  润新知