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状态。
总结: