【CAG_SLAM】-03-Padding-Modules
功能描述
对输入的图像信号进行裁边和填充0的操作,裁边的具体操作为,保持裁边后的图像的可以被分成整数个24*24(像素为单位)的block,裁边选择的方向是上边和右边的部分;裁边完成后,将图像四周用0进行padding,宽度为21像素。
每次进行像素的输入是通过每次输入4个像素(4*
8bit),一直输入完所有的像素,再进行裁边的操作。图像数据的大小可以提前知道,例如(1241*
376,1242*
375)两种情况。因此,可以等到所有数据输入完成再进行裁边,或者边输入,边裁边输出。
输入包括图像的大小(宽和高),fast_busy是否繁忙,若繁忙,则需要等待,还有一个有效的valid_i信号;
输出包括本模块的busy和valid,以及每次四个像素进行输出
实现方法
知道了图像的长和宽,就可以统计输入理论上需要的周期,在这个周期中,就可以采取计数器的方法,对其当前的信号计数从而确定坐标的位置(可以分别进行行、列坐标的计数),同时,有两种方式进行输出,一种是通过用寄存器数组记录下所有的数据,然后,再将这些数据最后一起输出;另一种方式是,判断当前坐标是否应该出现再输出的信号中,如果出现在输出信号中,应该出现在哪一个位置,有计数器判断其输出的时刻,做到一边输入一边输出。
具体实现
通过两种方案,输入完成后再输出和一边输入一边输出。
方案一实现
方法一实现过于简单粗暴,其会采用将会浪费大量的存储空间并且其效率也较低,会有大量的迟滞等待输入的时间,因此这里不予讨论。
方案二实现
方案二实现的效率和存储空间均有一定的改善,因此,这里进行详细的讨论。由于在输入端加入了控制读入的信号,因此,输出的逻辑变得简单了。
仅需要在输入信号中,pdg_busy_o为低电平、data_i输入为高电平,就按照clock输出当前输入的值即可,同时在输出也应该有输出有效的信号,需要在有效输出前先将其置高,然后再在输出无效时将其拉低。
方案二的实现中,涉及到使用一个触发器的问题,直接使用触发器会占用大量的资源。这里决定用多个周期计算,来减小资源的消耗。
除法器
这里需要一个除以24的除法器,如果直接使用verilog自带的除法运算,在进行取余操作时,会花费大量的资源,即使做到了单个时钟周期的延时。这里为了减少资源的消耗,将单个周期的运算化为多个运算,运用类似逐次比较的方法,加上不同的权(weight)后,比较其数值大小,进而得到最后的答案。
在每次开始比较时,将当前位先置为1,然后比较后,如果发现比被除数小,再进行取反操作置零。操作结束的标志是。
DEBUG-01
第一次写的逻辑总体上是正确的,但是在最后一次比较出现了一点问题。代码如下:
1 | // 这里的weight是不同位上为1的数(可以看作是二进制数的不同位的权值),但是我一开始对其定义的就是比result低了一位开始,导致当result去比较最低位的时候,权值weight已经变成0了。因此,需要将weight从result相同的位开始,进行比较、移位和判断。 |
case 详细讨论
这里的case情况的条件需要现在来进一步不详细分类讨论以确保完备和正确。
输入信号
valid_i | dividend_i |
---|
输出信号
result_o | remainder_o | valid_o | div_mod_busy_o |
---|
- 开始条件,当前输入信号有效(valid_i)且当前模块不忙(div_mod_busy_o)
- 开始操作,将输入信号(dividend_i)放入内部寄存器保存,模块忙信号置位(div_mod_busy_o)
- 结束计算条件,当前已经到达最低位的比较(counter_i == 0),输出信号无效(valid_o),且模块忙(div_mod_busy_o == 1’b1)
- 结束计算操作,计算余数(remainder)的值,将输出(valid_o)置为有效。
- 完成条件,当前的输出有效(valid_o),且模块忙(div_mod_busy_o == 1’b1)说明达到完成条件。
- 完成操作,valid_o置零,div_mode_busy_o置零。
- 进入计算条件,div_mode_busy_o为1,当前的counter不为零
- 进入计算操作,比较当前结果与输入值之间的大小,如果大,就置0;如果小,就置1(保持不变,加上下一个weight即可)。
- IDLE锁存,无变化。
完整代码
1 | module div_mod_n#( |
padding模块
发现直接用mod 模块的耗费和我设计的其实是差不多的。。。这里直接使用取余运算罢。
padding 模块的主要问题是每次不是一个像素、一个像素地输入。因此,在进行了裁边操作之后,如何提取有效像素是一个需要思考的问题。这里要提取有效像素,一个是需要确定开始输入时候的操作(处理掉前几行被裁减的部分),第二个是处理掉开始有效输入之后中的无效数据(每行被裁减的列的像素)。解决这两个问题需要用到两个存储单元(每个存放四个像素),一个用来记录当前应该输出的四个像素,一个用来存放下一个时刻应该输出的像素(不到4个,是上一次输入剩下来的部分,在下一次输入时,这部分被移入第一个存储单元中)。
在对第一个问题的方案的思考中,我们应该在输入的开始时进行计数,但记录到(cropped_row_num * col_num / 4(左移))时,说明下一个时刻的输入中,有第一个有效数据;为了提取出这里的第一个有数据时,需要知道上一行式子除法之后的余数是多少,这里刚好是对4作除法,因此,可以直接提取低两位作为这里的余数(mod_4),第一个有效数据就是下一个输入的(mod_4:4)的数据,将其放入第一个存储单元中,下一个开始全部都是有效数据,需要进行的处理是将前(0:mod_4-1)的数据放入第一个存储单元中,将后(mod_4:4)放入第二个存储单元中,至此第一个数据就可以进行输出了。
(从上图中可以看到,输入和输出之间会有一个周期的延迟,黑色是当下的输入,红色是下一次的输入)
但是这里的输出会遇到这样一个问题,输出也不是按4的整数倍开始的,有两种情况,第一种对应着奇数的行,例如对第一行有效数据的输出时,第一次输出的是0XXX这样的一组四个像素,在这一行有效输出结束的时候是输出的是X000的一组四个像素(其余都是zero-padding的部分了),最前后的两个像素合起来就是一个完整的一组四个像素,第二种对应着偶数行,和前面类似的情况,但这里第一次是000X,最后一次是XXX0。分别考虑这两种情况,我们也需要用到两个基本存储单元(每个存放4个像素,一个用于存放当前输出,一个存放下一次的输出)。
参考资料
verilog if后必须要有else吗_【逻辑】verilog中阻塞赋值和非阻塞赋值的区别_weixin_39877166的博客-CSDN博客
Verilog if-else-if (chipverify.com)
[Verilog语法之三:变量 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/72012739#:~:text= reg型数据可以赋正值,也可以赋负值。,但当一个reg型数据是一个表达式中的操作数时,它的值被当作是无符号值,即正值。 例如:当一个四位的寄存器用作表达式中的操作数时,如果开始寄存器被赋以值-1%2C则在表达式中进行运算时,其值被认为是%2B15。)[Verilog语法之三:变量 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/72012739#:~:text= reg型数据可以赋正值,也可以赋负值。,但当一个reg型数据是一个表达式中的操作数时,它的值被当作是无符号值,即正值。 例如:当一个四位的寄存器用作表达式中的操作数时,如果开始寄存器被赋以值-1%2C则在表达式中进行运算时,其值被认为是%2B15。)