数据的组包和拆包是FPGA设计中经常会遇到的应用场景,大家可以回顾下,上游模块发送来的数据,在本模块如果需要接收或者解析,这里就或多或少地会需要数据的拆包,然后才能获取有效信息,同时本模块如果想把数据发送到下游模块,这里也就不可避免地需要数据的组包,接着把组包后的数据逐一发送下去,因此不夸张地说数据的组包和拆包几乎伴随了整个FPGA工程。
下面先来介绍数据的拆包吧,即通俗地说就是把接收到的数据通过一定时序逻辑去赋值给其对应的信号量,从而提取出有效的数据信息。仅仅从书写形式上来讲,各类代码风格在处理数据拆包方面也大同小异,这里就用Gvim下的计数器模板举例说明。笔者从实际工作中归纳总结了三种常用的数据拆包书写形式,这基本可以满足大部分数据拆包的应用场景,当然如果朋友们有更好的设计方法也可以互相交流学习。
通过上面的分析,大家也清楚了对于数据拆包,其实说到底就是一个赋值信号量的操作,那么落实到代码层面我们要去如何进行赋值操作呢,一般数据拆包多数都会涉及到计数器的计数问题,下面结合了Gvim编辑器下的计数器模板,笔者逐一为大家介绍三种经典地书写形式即直接赋值法、移位赋值法、选择赋值法,可以说不同的书写形式,刚好可以满足不同的设计需求,当然这里也有些简洁实用的小技巧。
如图1到3所示,是笔者选取一些代码片段,分别采用了直接赋值法、移位赋值法、选择赋值法对数据进行了拆包,赋值信号量处理,大家可以清楚地看到三种赋值的方法从书写形式来说,各有各的特色:其中直接赋值法,简便直观,直接通过计数器计数,再根据计数器的值从高位到低位依次赋值给对应的信号量,这样的写法非常适合搭配Gvim下的计数器模板;移位赋值法几乎是行业默认的信号量赋值的方法,新来的数据首先放置在信号量最低位,然后通过逐次移位的操作,再去赋值给指定的信号量;选择赋值法在很多FPGA工程师的代码中频频出现,笔者也结合Gvim下的计数器模板设计了如下的书写形式,选择赋值法相比于前面的两种赋值方法,主要的优势在于这种写法非常地灵活,例如在大部分情况下,数据都是从高位到低位赋值的,但不排除有少数情况,数据是从低位到高位赋值的,甚至按不规则的顺序进行赋值的,这时候用选择赋值法在代码层面上就非常容易去实现。
三种经典地赋值方法供大家参考,可以结合不同应用场景和个人的代码书写习惯来进行选择,下面为了更加深入地说明三种赋值方法,笔者也通过实例和TimeGen下的波形图,把这三种方法的特点更加形象直观地展示给大家,也把更多实用小技巧告诉给大家。朋友们也可以试着用Givm去写一写,再用TimeGen去画一画。
一、直接赋值法
图1 直接赋值法去实现数据拆包举例
二、移位赋值法
图2 移位赋值法去实现数据拆包举例
三、选择赋值法
图3 选择赋值法去实现数据拆包举例
举的例子也非常简单,就是一个计数器cnt去从0到7依次去计数,然后定义了不同信号量去接收din_data数据总线的数据,其中rx_data1_1代表使用直接赋值法去赋值的信号量;rx_data2_1代表使用时序逻辑,移位赋值法去赋值的信号量;rx_data2_2代表使用组合逻辑,移位赋值法去赋值的信号量;rx_data3_1使用选择赋值法去从高位到低位赋值的信号量;rx_data3_2使用选择赋值法去从低位到高位赋值的信号量,整个实例的代码设计如图4所示,而对应信号的时序波形图如5所示,数据赋值完成后笔者都用红色进行标注了。
图4 三种赋值法去实现数据拆包例子
图5 三种赋值法去实现数据拆包例子中各个信号的波形图
通过上面的例子,笔者在这里也简答总结下三种赋值方法的各种特点:1.三种赋值的方法都可以实现同样赋值的效果,能看到rx_data1_1、rx_data2_1、rx_data3_1有异曲同工的效果;2.移位赋值法使用得更广泛,同时如果用组合逻辑结合Gvim下的计数器模板,移位赋值法可以实现和add_cnt同一时钟周期内赋值的效果,而这对于一些设计,便可以达到了“凑时序”的效果。因为在正常情况下都是使用时序逻辑去实现赋值操作,所以在end_cnt计数器计数完后的一个时钟周期信号量才被赋值完全,但一些应用需要在end_cnt结束的那个时钟周期,就对赋值信号量进行一些逻辑判断或者其他处理,这时候笔者就建议大家用rx_data2_2的赋值方式;3.选择赋值法,因为用了case语句,书写形式上最为灵活,可以实现数据从高位到低位赋值如rx_data3_1,也可以实现数据从低位到高位赋值如rx_data3_2,以及数据的不规则赋值。
与数据拆包相对应的,这里很多知识点也是非常类似的,如果说数据拆包是通过一定的时序逻辑进行信号量的接收赋值操作的话,那么数据组包则是通过一定的时序逻辑进行信号量的拼接发送操作,这里仅仅从字面的意思,相信也非常容易理解它们的区别和联系了,所以不再过多赘述了。