• 压缩算法:基于FPGA的Varint编码实现(附代码)


    一、概念

    什么是Varint编码呢?首先我们来介绍一下Varint编码,Varint编码就是一种用一个或多个字节将数据序列化,并对数据进行压缩的方法,因此也可以称之为Varint压缩算法。

    在进行数据传输过程,我们经常用大位宽来进行数据的传输。有时候是32位或者64位传输某个数据,然而,一直使用大位宽来传输数据也有它的缺点,比如传输很小的数据时,会造成资源的浪费。

    例如,我们要传送一个1,而用64位来传输的话就需要表示为00000000_00000000_00000000_00000000_00000000_000000000_00000000_00000001,用这样的方式来传输一个1需要消耗8Byte的存储,属实是很浪费存储空间,而使用Varint编码对它进行压缩后,我们只需要一个Byte就能将它传输出去,大大节省了存储空间,避免了资源的浪费。

    二、设计原理

    下面我们就来介绍一下Varint编码是如何对原有数据进行编码处理的。在介绍Varint编码原理之前,我们先介绍一下字节数据的两种排序方式,大端和小端。大端数据指的是将高位的数据存在低位的地址中,例如将0x01234567存入一个64位的寄存器reg,则存入高位reg[7]的是7,然后依次是reg[6]=6、reg[5]=5、reg[4]=4、reg[3]=3、reg[2]=2、reg[1]=1、reg[0]=0,即逆序存入寄存器中,这种方式就称之为大端序。小端序即反之,高位的数据存入高地址,低位的数据放入低地址。

    在这基础上我们再来讲Varint编码的原理,Varint编码使用的就是大端序。Varint编码将有无效数据去除,然后将效数据分成若干个组,每个组为8位,即一个字节,除去最后一个字节外,其余有效组最高位均为1,最后一个字节最高位为0。有效组最高位为1即代表这个字节后面还有有效数据组,当有效数据组最高位为0时则代表当前有效组为最后一个有效字节,除去最高位,其余位均为有效数据。

    我们可以举个例子来更加详细的说明这个原理。 仍然以64位数据为例,如00000000_00000000_00010001_11011001_00110011_10101001_11001100_00110011。编码步骤如下:

    (1)首先从最后一个字节开始进行编码,最后一个字节为00110011,按照编码规则我们取后七位,即截取0110011,因为后面还有数据,则最高位取1,然后与截取的有效数据组合在一起组成第一个有效数据组10110011,然后放在整个数据的最高位。

    (2)然后是第二个数据,同样往前取七位,得到0011000,同样在本组最高位补1,即得到10011000,组合第一个数据组则为10110011_10011000。

    (3)第三个数据,再往前取七位,得到0100111,在本有效数据组最高位补1,得到10100111,再拼接到前面的有效数据组之后,即10110011_10011000_10100111。

    (4)第四个数据,同样的方式往前取七位,得到0011101,最高位补1,得到10011101,继续拼接在有效数据组后面,即10110011_10011000_10100111_10011101。

    (5)第五个数据,再往前取七位,得到0010011,在最高位补1,得到10010011,继续往有效数据组后拼接,得到10110011_10011000_10100111_10011101_10010011。

    (6)第六个数据,按照上述方法,可得10111011,拼接后可得10110011_10011000_10100111_10011101_10010011_10111011。

    (7)第七个数据,取得0000100,由观察得知,这个有效数据组之后均为0,即有效数据已全部截取完毕,则按照Varint编码规则,最高位补0,完成编码,将数据全部拼接后得到进行Varint编码后的数据,即10110011_10011000_10100111_10011101_10010011_10111011_00000100。

    将上述进行Varint编码后得到的有效数据组与原数据相比,节省了一个字节的存储资源。解码只要将上述过程逆序进行即可,这里就不过多赘述。熟悉完了Varint编码的原理,下面我们就可以开始进行设计了。

    三、架构设计

    设计架构如下图:

     

    将本设计模块命名为varint_encode,clk为输入时钟,rst_n为复位信号,idata为64位是输入数据,ivalid为数据有效信号,odata0~odata7为输出的有效数据,ovalid0~ovalid7为伴随输出有效数据的数据有效信号。由于FPGA输出的数据位宽都是固定的,因此需要将各个压缩后的位宽都定义一遍。

    四、Verilog 代码实现

    功能模块实现代码如下:

    1. module varint_encode(
    2. input wire clk,
    3. input wire rst_n,
    4. input wire [63:0] idata,
    5. input wire ivalid,
    6. output reg [63:0] odata0,
    7. output reg [55:0] odata1,
    8. output reg [47:0] odata2,
    9. output reg [39:0] odata3,
    10. output reg [31:0] odata4,
    11. output reg [23:0] odata5,
    12. output reg [15:0] odata6,
    13. output reg [7:0] odata7,
    14. output reg ovalid0,
    15. output reg ovalid1,
    16. output reg ovalid2,
    17. output reg ovalid3,
    18. output reg ovalid4,
    19. output reg ovalid5,
    20. output reg ovalid6,
    21. output reg ovalid7
    22. );
    23. reg [63:0] idata_r;
    24. reg [7:0] buff0;
    25. reg [7:0] buff1;
    26. reg [7:0] buff2;
    27. reg [7:0] buff3;
    28. reg [7:0] buff4;
    29. reg [7:0] buff5;
    30. reg [7:0] buff6;
    31. reg [7:0] buff7;
    32. reg [7:0] buff8;
    33. reg [7:0] buff9;
    34. reg buff0_req;
    35. reg buff1_req;
    36. reg buff2_req;
    37. reg buff3_req;
    38. reg buff4_req;
    39. reg buff5_req;
    40. reg buff6_req;
    41. reg buff7_req;
    42. reg buff8_req;
    43. reg buff9_req;
    44. always @ (posedge clk,negedge rst_n) begin
    45. if (rst_n == 1'b0)
    46. idata_r <= 64'd0;
    47. else
    48. if (ivalid == 1'b1)
    49. idata_r <= idata;
    50. else
    51. idata_r <= idata_r;
    52. end
    53. always @ (posedge clk,negedge rst_n) begin
    54. if (rst_n == 1'b0) begin
    55. buff0 <= 8'd0;
    56. buff0_req <= 1'b0;
    57. end
    58. else
    59. if (idata_r[6:0] > 0) begin
    60. buff0 <= {1'b1,idata_r[6:0]};
    61. buff0_req <= 1'b1;
    62. end
    63. else begin
    64. buff0 <= 8'd0;
    65. buff0_req <= 1'b0;
    66. end
    67. end
    68. always @ (posedge clk,negedge rst_n) begin
    69. if (rst_n == 1'b0) begin
    70. buff1 <= 8'd0;
    71. buff1_req <= 1'b0;
    72. end
    73. else
    74. if (idata_r[13:7] > 0) begin
    75. buff1 <= {1'b1,idata_r[13:7]};
    76. buff1_req <= 1'b1;
    77. end
    78. else begin
    79. buff1 <= 8'd0;
    80. buff1_req <= 1'b0;
    81. end
    82. end
    83. always @ (posedge clk,negedge rst_n) begin
    84. if (rst_n == 1'b0) begin
    85. buff2 <= 8'd0;
    86. buff2_req <= 1'b0;
    87. end
    88. else
    89. if (idata_r[20:14] > 0) begin
    90. buff2 <= {1'b1,idata_r[20:14]};
    91. buff2_req <= 1'b1;
    92. end
    93. else begin
    94. buff2 <= 8'd0;
    95. buff2_req <= 1'b0;
    96. end
    97. end
    98. always @ (posedge clk,negedge rst_n) begin
    99. if (rst_n == 1'b0) begin
    100. buff3 <= 8'd0;
    101. buff3_req <= 1'b0;
    102. end
    103. else
    104. if (idata_r[27:21] > 0) begin
    105. buff3 <= {1'b1,idata_r[27:21]};
    106. buff3_req <= 1'b1;
    107. end
    108. else begin
    109. buff3 <= 8'd0;
    110. buff3_req <= 1'b0;
    111. end
    112. end
    113. always @ (posedge clk,negedge rst_n) begin
    114. if (rst_n == 1'b0) begin
    115. buff4 <= 8'd0;
    116. buff4_req <= 1'b0;
    117. end
    118. else
    119. if (idata_r[34:28] > 0) begin
    120. buff4 <= {1'b1,idata_r[34:28]};
    121. buff4_req <= 1'b1;
    122. end
    123. else begin
    124. buff4 <= 8'd0;
    125. buff4_req <= 1'b0;
    126. end
    127. end
    128. always @ (posedge clk,negedge rst_n) begin
    129. if (rst_n == 1'b0) begin
    130. buff5 <= 8'd0;
    131. buff5_req <= 1'b0;
    132. end
    133. else
    134. if (idata_r[41:35] > 0) begin
    135. buff5 <= {1'b1,idata_r[41:35]};
    136. buff5_req <= 1'b1;
    137. end
    138. else begin
    139. buff5 <= 8'd0;
    140. buff5_req <= 1'b0;
    141. end
    142. end
    143. always @ (posedge clk,negedge rst_n) begin
    144. if (rst_n == 1'b0) begin
    145. buff6 <= 8'd0;
    146. buff6_req <= 1'b0;
    147. end
    148. else
    149. if (idata_r[48:42] > 0) begin
    150. buff6 <= {1'b1,idata_r[48:42]};
    151. buff6_req <= 1'b1;
    152. end
    153. else begin
    154. buff6 <= 8'd0;
    155. buff6_req <= 1'b0;
    156. end
    157. end
    158. always @ (posedge clk,negedge rst_n) begin
    159. if (rst_n == 1'b0) begin
    160. buff7 <= 8'd0;
    161. buff7_req <= 1'b0;
    162. end
    163. else
    164. if (idata_r[55:49] > 0) begin
    165. buff7 <= {1'b1,idata_r[55:49]};
    166. buff7_req <= 1'b1;
    167. end
    168. else begin
    169. buff7 <= 8'd0;
    170. buff7_req <= 1'b0;
    171. end
    172. end
    173. always @ (posedge clk,negedge rst_n) begin
    174. if (rst_n == 1'b0) begin
    175. buff8 <= 8'd0;
    176. buff8_req <= 1'b0;
    177. end
    178. else
    179. if (idata_r[62:56] > 0) begin
    180. buff8 <= {1'b1,idata_r[62:56]};
    181. buff8_req <= 1'b1;
    182. end
    183. else begin
    184. buff8 <= 8'd0;
    185. buff8_req <= 1'b0;
    186. end
    187. end
    188. always @ (posedge clk,negedge rst_n) begin
    189. if (rst_n == 1'b0) begin
    190. buff9 <= 8'd0;
    191. buff9_req <= 1'b0;
    192. end
    193. else
    194. if (idata_r[63] == 1'b1) begin
    195. buff9 <= {1'b0,5'b00000,idata_r[63]};
    196. buff9_req <= 1'b1;
    197. end
    198. else begin
    199. buff9 <= 8'd0;
    200. buff9_req <= 1'b0;
    201. end
    202. end
    203. always @ (posedge clk,negedge rst_n) begin
    204. if (rst_n == 1'b0) begin
    205. odata0 <= 64'd0;
    206. odata1 <= 56'd0;
    207. odata2 <= 48'd0;
    208. odata3 <= 40'd0;
    209. odata4 <= 32'd0;
    210. odata5 <= 24'd0;
    211. odata6 <= 16'd0;
    212. odata7 <= 8'd0;
    213. ovalid0 <= 1'b0;
    214. ovalid1 <= 1'b0;
    215. ovalid2 <= 1'b0;
    216. ovalid3 <= 1'b0;
    217. ovalid4 <= 1'b0;
    218. ovalid5 <= 1'b0;
    219. ovalid6 <= 1'b0;
    220. ovalid7 <= 1'b0;
    221. end
    222. else
    223. casex({buff0_req,buff1_req,buff2_req,buff3_req,buff4_req,buff5_req,buff6_req,buff7_req})
    224. 8'b0000_0000 : begin
    225. odata0 <= 64'd0; ovalid0 <= 1'b0;
    226. odata1 <= 56'd0; ovalid1 <= 1'b0;
    227. odata2 <= 48'd0; ovalid1 <= 1'b0;
    228. odata3 <= 40'd0; ovalid1 <= 1'b0;
    229. odata4 <= 32'd0; ovalid1 <= 1'b0;
    230. odata5 <= 24'd0; ovalid1 <= 1'b0;
    231. odata6 <= 16'd0; ovalid1 <= 1'b0;
    232. odata7 <= 8'd0; ovalid1 <= 1'b0;
    233. end
    234. 8'b1000_0000 : begin
    235. odata0 <= 64'd0; ovalid0 <= 1'b0;
    236. odata1 <= 56'd0; ovalid1 <= 1'b0;
    237. odata2 <= 48'd0; ovalid1 <= 1'b0;
    238. odata3 <= 40'd0; ovalid1 <= 1'b0;
    239. odata4 <= 32'd0; ovalid1 <= 1'b0;
    240. odata5 <= 24'd0; ovalid1 <= 1'b0;
    241. odata6 <= 16'd0; ovalid1 <= 1'b0;
    242. odata7 <= buff0; ovalid7 <= 1'b1;
    243. end
    244. 8'bx100_0000 : begin
    245. odata0 <= 64'd0; ovalid0 <= 1'b0;
    246. odata1 <= 56'd0; ovalid1 <= 1'b0;
    247. odata2 <= 48'd0; ovalid1 <= 1'b0;
    248. odata3 <= 40'd0; ovalid1 <= 1'b0;
    249. odata4 <= 32'd0; ovalid1 <= 1'b0;
    250. odata5 <= 24'd0; ovalid1 <= 1'b0;
    251. odata6 <= {buff0,buff1}; ovalid6 <= 1'b1;
    252. odata7 <= 8'd0; ovalid7 <= 1'b0;
    253. end
    254. 8'bxx10_0000 : begin
    255. odata0 <= 64'd0; ovalid0 <= 1'b0;
    256. odata1 <= 56'd0; ovalid1 <= 1'b0;
    257. odata2 <= 48'd0; ovalid1 <= 1'b0;
    258. odata3 <= 40'd0; ovalid1 <= 1'b0;
    259. odata4 <= 32'd0; ovalid1 <= 1'b0;
    260. odata5 <= {buff0,buff1,buff2}; ovalid5 <= 1'b1;
    261. odata6 <= 16'd0; ovalid1 <= 1'b0;
    262. odata7 <= 8'd0; ovalid7 <= 1'b0;
    263. end
    264. 8'bxxx1_0000 : begin
    265. odata0 <= 64'd0; ovalid0 <= 1'b0;
    266. odata1 <= 56'd0; ovalid1 <= 1'b0;
    267. odata2 <= 48'd0; ovalid1 <= 1'b0;
    268. odata3 <= 40'd0; ovalid1 <= 1'b0;
    269. odata4 <= {buff0,buff1,buff2,buff3}; ovalid4 <= 1'b1;
    270. odata5 <= 24'd0; ovalid1 <= 1'b0;
    271. odata6 <= 16'd0; ovalid1 <= 1'b0;
    272. odata7 <= 8'd0; ovalid7 <= 1'b0;
    273. end
    274. 8'bxxxx_1000 : begin
    275. odata0 <= 64'd0; ovalid0 <= 1'b0;
    276. odata1 <= 56'd0; ovalid1 <= 1'b0;
    277. odata2 <= 48'd0; ovalid1 <= 1'b0;
    278. odata3 <= {buff0,buff1,buff2,buff3,buff4}; ovalid3 <= 1'b1;
    279. odata4 <= 32'd0; ovalid1 <= 1'b0;
    280. odata5 <= 24'd0; ovalid1 <= 1'b0;
    281. odata6 <= 16'd0; ovalid1 <= 1'b0;
    282. odata7 <= 8'd0; ovalid7 <= 1'b0;
    283. end
    284. 8'bxxxx_x100 : begin
    285. odata0 <= 64'd0; ovalid0 <= 1'b0;
    286. odata2 <= {buff0,buff1,buff2,buff3,buff4,buff5}; ovalid2 <= 1'b1;
    287. odata3 <= 40'd0; ovalid1 <= 1'b0;
    288. odata4 <= 32'd0; ovalid1 <= 1'b0;
    289. odata5 <= 24'd0; ovalid1 <= 1'b0;
    290. odata6 <= 16'd0; ovalid1 <= 1'b0;
    291. odata7 <= 8'd0; ovalid1 <= 1'b0;
    292. end
    293. 8'bxxxx_xx10 : begin
    294. odata0 <= 64'd0; ovalid0 <= 1'b0;
    295. odata1 <= {buff0,buff1,buff2,buff3,buff4,buff5,buff6}; ovalid1 <= 1'b1;
    296. odata2 <= 48'd0; ovalid1 <= 1'b0;
    297. odata3 <= 40'd0; ovalid1 <= 1'b0;
    298. odata4 <= 32'd0; ovalid1 <= 1'b0;
    299. odata5 <= 24'd0; ovalid1 <= 1'b0;
    300. odata6 <= 16'd0; ovalid1 <= 1'b0;
    301. odata7 <= 8'd0; ovalid1 <= 1'b0;
    302. end
    303. 8'bxxxx_xxx1 : begin
    304. odata0 <= {buff0,buff1,buff2,buff3,buff4,buff5,buff6,buff7}; ovalid0 <= 1'b1;
    305. odata1 <= 56'd0; ovalid1 <= 1'b0;
    306. odata2 <= 48'd0; ovalid1 <= 1'b0;
    307. odata3 <= 40'd0; ovalid1 <= 1'b0;
    308. odata4 <= 32'd0; ovalid1 <= 1'b0;
    309. odata5 <= 24'd0; ovalid1 <= 1'b0;
    310. odata6 <= 16'd0; ovalid1 <= 1'b0;
    311. odata7 <= 8'd0; ovalid1 <= 1'b0;
    312. end
    313. default : begin
    314. odata0 <= {buff0,buff1,buff2,buff3,buff4,buff5,buff6,buff7}; ovalid0 <= 1'b1;
    315. odata1 <= 56'd0; ovalid1 <= 1'b0;
    316. odata2 <= 48'd0; ovalid1 <= 1'b0;
    317. odata3 <= 40'd0; ovalid1 <= 1'b0;
    318. odata4 <= 32'd0; ovalid1 <= 1'b0;
    319. odata5 <= 24'd0; ovalid1 <= 1'b0;
    320. odata6 <= 16'd0; ovalid1 <= 1'b0;
    321. odata7 <= 8'd0; ovalid1 <= 1'b0;
    322. end
    323. endcase
    324. end
    325. endmodule

    五、仿真测试及结果

    仿真测试代码如下:

    1. `timescale 1ns/1ps
    2. module varint_encode_tb;
    3. reg clk;
    4. reg rst_n;
    5. reg ivalid;
    6. reg [63:0] idata;
    7. wire [63:0] odata0;
    8. wire [55:0] odata1;
    9. wire [47:0] odata2;
    10. wire [39:0] odata3;
    11. wire [31:0] odata4;
    12. wire [23:0] odata5;
    13. wire [15:0] odata6;
    14. wire [7:0] odata7;
    15. wire ovalid0;
    16. wire ovalid1;
    17. wire ovalid2;
    18. wire ovalid3;
    19. wire ovalid4;
    20. wire ovalid5;
    21. wire ovalid6;
    22. wire ovalid7;
    23. varint_encode varint_encode_inst(
    24. .clk (clk),
    25. .rst_n (rst_n),
    26. .idata (idata),
    27. .ivalid (ivalid),
    28. .odata0 (odata0),
    29. .odata1 (odata1),
    30. .odata2 (odata2),
    31. .odata3 (odata3),
    32. .odata4 (odata4),
    33. .odata5 (odata5),
    34. .odata6 (odata6),
    35. .odata7 (odata7),
    36. .ovalid0 (ovalid0),
    37. .ovalid1 (ovalid1),
    38. .ovalid2 (ovalid2),
    39. .ovalid3 (ovalid3),
    40. .ovalid4 (ovalid4),
    41. .ovalid5 (ovalid5),
    42. .ovalid6 (ovalid6),
    43. .ovalid7 (ovalid7)
    44. );
    45. initial clk = 1'b0;
    46. always # 10 clk = ~clk;
    47. initial begin
    48. rst_n = 1'b0;
    49. ivalid = 1'b0;
    50. idata = 64'd0;
    51. # 201;
    52. rst_n = 1'b1;
    53. # 200;
    54. @ (posedge clk);
    55. # 2;
    56. idata = 64'b00000000_00000000_00010001_11011001_00110011_10101001_11001100_00110011;
    57. ivalid = 1'b1;
    58. @ (posedge clk);
    59. # 2;
    60. idata = 64'd0;
    61. ivalid = 1'b0;
    62. @ (posedge clk);
    63. # 2;
    64. idata = 64'b00000000_00000001_00010001_11011001_00110011_10101001_11001100_00110011;
    65. ivalid = 1'b1;
    66. @ (posedge clk);
    67. # 2;
    68. idata = 64'd0;
    69. ivalid = 1'b0;
    70. @ (posedge clk);
    71. # 2;
    72. idata = 64'b00000000_00000000_00000001_11011001_00110011_10101001_11001100_00110011;
    73. ivalid = 1'b1;
    74. @ (posedge clk);
    75. # 2;
    76. idata = 64'd0;
    77. ivalid = 1'b0;
    78. @ (posedge clk);
    79. # 2;
    80. idata = 64'b00000000_00000000_00000000_00000001_00110011_10101001_11001100_00110011;
    81. ivalid = 1'b1;
    82. @ (posedge clk);
    83. # 2;
    84. idata = 64'd0;
    85. ivalid = 1'b0;
    86. @ (posedge clk);
    87. # 2;
    88. idata = 64'b00000000_00000000_00000000_00000000_00000000_10101001_11001100_00110011;
    89. ivalid = 1'b1;
    90. @ (posedge clk);
    91. # 2;
    92. idata = 64'd0;
    93. ivalid = 1'b0;
    94. # 2000;
    95. $stop;
    96. end
    97. endmodule

    五、总结

    在进行原理理解与设计实现的时候,需要注意,逆序是字节的逆序,并非每一bit的数据都要进行逆序,且最高位是补位,代表后面还有无数据,并非是实际数据,在进行解码的时候要注意去掉每一个有效数据组的最高位,再进行拼接,这样得到的数据才是正确的数据,否则得到的将是错误数据。考虑到FPGA位宽定义的局限性,需要对每一个可能性的位宽大小均进行定义,并且定义一个相应的脉冲信号,告诉后级模块哪一个数据是有效的,这样设计才不会出错,否则输出的大小与原来输入的大小相同,也就失去了设计的意义。

  • 相关阅读:
    mysql使用orderby 不起作用
    1143. 最长公共子序列
    18 - 如何设置线程池大小?
    通过matlab对比遗传算法优化前后染色体的变化情况
    Windows软件架构概念
    小技巧--使用异或来替换原本的常量交换
    whistle 的使用
    计算机网络 | 网络层
    基于51的单片机GPS定位系统设计
    不同数据库行号关联问题
  • 原文地址:https://blog.csdn.net/ONEFPGA/article/details/125540541