qspi_monitor有两个ana_port端口,将qspi总线上收集到的数据分别发送给scoreboard和coverage model。
其run_phase中主要运行代码放在foever块中,并在每个时钟周期进行状态机的判断及跳转。device_state默认设置为IDLE,device_state的状态包括IDLE、COMMAND、ADDR、RDATA_DUMMY、WDATA_DUMMY、WDATA、RDATA。在command命令中又根据命令形式,设置有四个状态,分别为WRITE_ENABLE、WRITE_DISABLE、READ_DATA、PAGE_PROGRAMMING以及默认default,一般来说不会执行到default。
由于QSPI总线传输的数据格式为idle——COMMAND——ADDR——DUMMY——DATA——idle,因此,monitor在监控数据时候状态机的跳转也主要遵循此数据格式的规律。
任何时候开始传输时,monitor监控到的初始状态都是IDLE,
从IDLE开始,通过判断vif接口上的commond_width命令宽度,从而决定是否跳转至COMMOND状态,如果commond_width不为0,那么就跳转至COMMOND状态,将总线上数据位数收集够(收集位数=commond_width),然后判断此收集到的command命令所代表的命令类型,根据命令类型进行判断及状态跳转。
如果command是WRITE_ENABLE或WRITE_DISABLE,直接将command命令赋值到qspi_txn事务中,并广播发送出去(因为在这两种模式下,总线上只发送command命令,不发送其他任何数据)。
如果command是PAGE_PROGRAMMING,那么根据总线数据格式,下一步应该要发送地址,此时就要判断vif上地址的位数是否为0,如果不是0,那么就要在下个周期跳转到ADDR状态去接收地址。如果是0,那么根据数据格式,下一步应该要等待DUMMY周期,所以就需要判断dummy周期是否为0,如果不是0,就在下一周期跳转至WDATA_ADDR_DUMMY状态,如果dummy周期是0,那么根据数据格式,下一步应该要发送数据,所以下一个状态跳转到WDATA状态。
如果command是READ_DATA,那么根据数据格式,下一步应该发送地址,此时就要判断vif上地址宽度是否为0,如果不是0,那么下一个周期就应该直接跳转到ADDRE状态,如果是0,那么根据数据格式,下一步应该发送dummy周期,所以就要判断dummy周期是否为0,如果dummy周期不是0,那么下一个周期就应该跳转至RDATA_ADDR_DUMMY状态,如果dummy周期是0,那么根据数据格式,下一步应该发送rdata数据,所以下一个周期应该跳转至RDATA状态。
从IDLE开始,如果vif上的commond_width为0,且vif上的addr_width不为0,也就是没有command命令,直接进入ADDR状态,在此状态从总线接收地址数据,然后判断是写还是读,不管是读写,都需要先判断dummy周期再决定跳转至WDATA_ADDR_DUMMY\RDATA_ADDR_DUMMY状态还是RDATA\WDATA状态。
从IDLE开始,如果vif上的commond_width为0,且vif上的addr_width为0,那么就直接判断dummy周期是否为0以及是读还是写来决定跳转的状态。
在IDLE、ADDR、WDATA、RDATA状态都需要从总线收集数据。在每个状态机中收集并计数,与vif上的数据比较,从而确定是否收集结束并进行状态跳转,如果没有收集结束,状态机不会跳转,那么下一个周期会继续收集数据。DUMMY状态由于没有数据,所以只是空打拍计数,不从总线收集数据(在vif接口里面定义了command_width、addr_width、data_width、rdata_dummy、rdata_addr_dummy、wdata_addr_dummy的默认值,如果在seq中需要传输特殊格式的数据,那么需要在seq中获取接口,并将接口中对应数据默认值进行修改,从而可以保证qspi_monitor可以平稳运行)
每次将全部数据格式的所有状态机跳转一遍(width为0的跳过),最后回到IDLE,此时将期间收集到事务中的command、addr、wdata\rdata都write出去。
device与qspi_monitor的代码几乎一致,仅有个别地方不一样,不同之处在于device将总线送来的数据存在本地的[31:0] data [int]关联数组中,然后在主机读取数据的时候,将数据驱动到端口上。device设备上除了声明了qspi的端口通过qspi的IP与主机相连,还声明了command_width、addr_width、data_width、rdata_dummy、rdata_addr_dummy、wdata_addr_dummy端口与qspi的IP上的对应端口相连,目的就是使得device内部方便判断并进行状态跳转。
不论是在device中还是qspi_monitor中,data数据每32位存一次,超过32为就将addr+4就再存。
monitor如何收集总线上数据?
总线上有数据输出的时候,spi_csn为0、spi_oe为1,spi_sdo就是总线上的输出信号,当spi_csn为0、spi_oe为1的时候,就将spi_sdo输出信号收集到sample_data中去,同时每收集一个信号就计数一次,此计数值用来为后续command、addr等信号移位及赋值。
总线上有数据输入的时候,操作类似。
device如何驱动数据?
根据几个csn信号是1来判断是4线还是单线模式,如果是单线模式,就把最高位一次输出,如果是四线,则每次输出最高4位。每次输出1bit数据,计数+1,输出4bit数据,计数+4,将计数不断累加得到一共输出多少,与数据宽度对比是否输出完毕,如果超过32位,则每32位,地址+4.
注意点:当commond长度为0时候,此时不知道具体是读还是写,因此在接口上设置了一个标志位spi_rw,可以设置为0/1表示读写。monitor在IDLE状态检测到command为0,如果地址不是0,那么下一个状态跳转就是到ADDR状态,而此ADDR并不清楚是读的地址还是写的地址,因此在IDLE中检测到comm是0时候就将接口中的默认标志位引入,可以是读,也可以是写(正常默认为写)。device也是如此。command不为0时候,在读写命令中同样会设置标志位以便在ADDR状态作出跳转选择。