├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── pack_i32.v ├── pack_i64.v ├── pack_u32.v ├── pack_u64.v ├── test ├── assert.vh ├── unpack_i32_tb.v ├── unpack_i64_tb.v └── unpack_u32_tb.v ├── unpack_signed.v └── unpack_unsigned.v /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Aliaksei Chapyzhenka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC = . 2 | BUILD = build 3 | 4 | IVERILOG = iverilog -I $(SRC) -y $(SRC) 5 | VVP = vvp -N 6 | 7 | 8 | .PHONY: all clean 9 | 10 | 11 | all: 12 | 13 | clean: 14 | rm -rf $(BUILD) *~ 15 | 16 | test: test/unpack_i32 test/unpack_i64 test/unpack_u32 17 | 18 | 19 | # 20 | # Auxiliar objectives 21 | # 22 | $(BUILD): 23 | mkdir -p $(BUILD) 24 | 25 | 26 | # 27 | # Test and view rules 28 | # 29 | test/unpack_i32: $(BUILD)/unpack_i32_tb.vcd 30 | test/unpack_i64: $(BUILD)/unpack_i64_tb.vcd 31 | test/unpack_u32: $(BUILD)/unpack_u32_tb.vcd 32 | 33 | $(BUILD)/%.vcd: $(BUILD)/% $(BUILD) 34 | (cd $(BUILD) && $(VVP) ../$<) || (rm $< && exit 1) 35 | 36 | $(BUILD)/%_tb: $(SRC)/unpack_signed.v $(SRC)/unpack_unsigned.v test/assert.vh test/%_tb.v 37 | $(IVERILOG) -I test test/$(@F).v $< -o $@ 38 | 39 | view/%: $(BUILD)/%_tb.vcd test/% 40 | gtkwave $< 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LEB128 2 | 3 | Little Endian Base 128 ([LEB128](https://en.wikipedia.org/wiki/LEB128)) 4 | converters in Verilog. 5 | 6 | ## Overview 7 | 8 | Encoding procedure. 9 | 10 | ### Unsigned 11 | 12 | ``` 13 | 624485 14 | 0x00098765 HEX 15 | 00000000_00001001_10000111_01100101 binary 16 | 0100110_0001110_1100101 shrink to a multiple of 7 bits 17 | 00100110 10001110 11100101 Add high 1 bits on all but last group to form bytes 18 | 0x26 0x8E 0xE5 HEX 19 | 0xE5 0x8E 0x26 (0x00 0x00) Output byte stream 20 | ``` 21 | 22 | ### Signed 23 | 24 | ``` 25 | -624485 26 | 0xFFF6789b HEX 27 | 11111111_11110110_01111000_10011011 two's complement binary 28 | 1011001_1110001_0011011 shrink to a multiple of 7 bits (1+ sign bit included) 29 | 01011001 11110001 10011011 Add high 1 bits on all but last group to form bytes 30 | 0x59 0xf1 0x9b HEX 31 | 0x9b 0xf1 0x59 (0x00 0x00) Output byte stream 32 | ``` 33 | -------------------------------------------------------------------------------- /pack_i32.v: -------------------------------------------------------------------------------- 1 | module pack_i32 (); 2 | endmodule 3 | -------------------------------------------------------------------------------- /pack_i64.v: -------------------------------------------------------------------------------- 1 | module pack_i64 (); 2 | endmodule 3 | -------------------------------------------------------------------------------- /pack_u32.v: -------------------------------------------------------------------------------- 1 | module pack_u32 ( 2 | input [31:0] i, 3 | output reg [7:0] o0, o1, o2, o3, o4, 4 | output reg [2:0] len 5 | ); 6 | 7 | reg [6:0] c0, c1, c2, c3, c4; 8 | reg [4:0] co; 9 | reg [4:0] gl; 10 | 11 | // i = $.wire() 12 | always @* begin 13 | // c = i.chop(7) // chunks 14 | {c4, c3, c2, c1, c0} = i; 15 | // co = c.reduce.or() // flags 16 | co[0] = (|c0); 17 | co[1] = (|c1); 18 | co[2] = (|c2); 19 | co[3] = (|c3); 20 | co[4] = (|c4); 21 | // gl = co.expand() // glue bits 22 | gl[0] = co[4] | co[3] | co[2] | co[1]; 23 | gl[1] = co[4] | co[3] | co[2]; 24 | gl[2] = co[4] | co[3]; 25 | gl[3] = co[4]; 26 | gl[4] = 1'b0; 27 | // len = gl.decode() 28 | len[0] = gl[0] | gl[2] | gl[4]; 29 | len[1] = gl[1] | gl[2]; 30 | len[2] = gl[4] | gl[5]; 31 | // o = gl.right(c) 32 | o0 = {gl[0], c0}; 33 | o1 = {gl[1], c1}; 34 | o2 = {gl[2], c2}; 35 | o3 = {gl[3], c3}; 36 | o4 = {gl[4], c4}; 37 | end 38 | 39 | endmodule 40 | -------------------------------------------------------------------------------- /pack_u64.v: -------------------------------------------------------------------------------- 1 | module pack_u64 (); 2 | 3 | endmodule 4 | -------------------------------------------------------------------------------- /test/assert.vh: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on code from http://stackoverflow.com/a/31302223/586382 3 | */ 4 | `define assert(signal, value) \ 5 | if (signal !== value) begin \ 6 | $display("ASSERTION FAILED in %m:%d: signal != value", `__LINE__); \ 7 | $stop; \ 8 | end 9 | -------------------------------------------------------------------------------- /test/unpack_i32_tb.v: -------------------------------------------------------------------------------- 1 | `include "assert.vh" 2 | 3 | 4 | module unpack_i32_tb (); 5 | 6 | reg [0:39] in; 7 | wire [31:0] out; 8 | wire [ 2:0] len; 9 | 10 | unpack_signed #(.N(32)) DUT(in, out, len); 11 | 12 | initial begin 13 | $dumpfile("unpack_i32_tb.vcd"); 14 | $dumpvars(0, unpack_i32_tb); 15 | 16 | in = 40'h2axxxxxxxx; 17 | #1 18 | `assert(out, 42); 19 | `assert(len, 1); 20 | 21 | in = 0; 22 | #1 23 | `assert(out, 0); 24 | `assert(len, 1); 25 | 26 | in = 40'h9bf1590000; 27 | #1 28 | `assert(out, -624485); 29 | `assert(len, 3); 30 | 31 | in = 40'hffffffff0f; 32 | #1 33 | `assert(out, -1); 34 | `assert(len, 5); 35 | 36 | $display("ok"); 37 | $finish; 38 | end 39 | 40 | endmodule // unpack_i32_tb 41 | -------------------------------------------------------------------------------- /test/unpack_i64_tb.v: -------------------------------------------------------------------------------- 1 | `include "assert.vh" 2 | 3 | 4 | module unpack_i64_tb (); 5 | 6 | reg [79:0] in; 7 | wire [63:0] out; 8 | wire [ 3:0] len; 9 | 10 | unpack_signed #(.N(64)) DUT(in, out, len); 11 | 12 | initial begin 13 | $dumpfile("unpack_i64_tb.vcd"); 14 | $dumpvars(0, unpack_i64_tb); 15 | 16 | // Ignore unset bits after value 17 | in = 80'h01xxxxxxxxxxxxxxxxxx; 18 | #1 19 | `assert(out, 1); 20 | `assert(len, 1); 21 | 22 | // Decode negative numbers 23 | in = 80'hffffffffffffffffff01; 24 | #1 25 | `assert(out, -1); 26 | `assert(len, 10); 27 | 28 | // Ignore data after value 29 | in = 80'h808080800cbc0b000000; 30 | // in = 80'h808080800cdeadbeef00; 31 | #1 32 | `assert(out, 32'hc0000000); 33 | `assert(len, 5); 34 | 35 | in = 80'h80808080800c00808080; 36 | #1 37 | `assert(out, 64'h6000000000); 38 | `assert(len, 6); 39 | 40 | in = 80'h8080808080800c008080; 41 | #1 42 | `assert(out, 64'h300000000000); 43 | `assert(len, 7); 44 | 45 | in = 80'h808080808080800c0080; 46 | #1 47 | `assert(out, 64'h18000000000000); 48 | `assert(len, 8); 49 | 50 | in = 80'h80808080808080800c00; 51 | #1 52 | `assert(out, 64'h0c00000000000000); 53 | `assert(len, 9); 54 | 55 | in = 80'h8080808080808080c001; 56 | #1 57 | `assert(out, 64'hc000000000000000); 58 | `assert(len, 10); 59 | 60 | $display("ok"); 61 | $finish; 62 | end 63 | 64 | endmodule // unpack_u64_tb 65 | -------------------------------------------------------------------------------- /test/unpack_u32_tb.v: -------------------------------------------------------------------------------- 1 | `include "assert.vh" 2 | 3 | 4 | module unpack_u32_tb (); 5 | 6 | reg [0:39] in; 7 | wire [31:0] out; 8 | wire [ 2:0] len; 9 | 10 | unpack_unsigned #(.N(32)) DUT(in, out, len); 11 | 12 | initial begin 13 | $dumpfile("unpack_u32_tb.vcd"); 14 | $dumpvars(0, unpack_u32_tb); 15 | 16 | in = 0; 17 | #1 18 | `assert(out, 0); 19 | `assert(len, 1); 20 | 21 | in = 40'h2a00000000; 22 | #1 23 | `assert(out, 42); 24 | `assert(len, 1); 25 | 26 | in = 40'he58e260000; 27 | #1 28 | `assert(out, 624485); 29 | `assert(len, 3); 30 | 31 | in = 40'hffffffff0f; 32 | #1 33 | `assert(out, 32'hffffffff); 34 | `assert(len, 5); 35 | 36 | $display("ok"); 37 | $finish; 38 | end 39 | 40 | endmodule // unpack_u32_tb 41 | -------------------------------------------------------------------------------- /unpack_signed.v: -------------------------------------------------------------------------------- 1 | module unpack_signed #( 2 | parameter N = 64 3 | ) 4 | ( 5 | input [ 0:M-1] in, 6 | output reg [ N-1: 0] out, 7 | output wire [$clog2(MB)-1: 0] len 8 | ); 9 | 10 | localparam MB = N/7+1; 11 | localparam M = MB*8; 12 | 13 | wire[N-1:0] unsigned_output; 14 | 15 | integer i; 16 | reg [MB-1:0] gl, ub, ho, sb; 17 | 18 | unpack_unsigned #(.N(N)) u64(in, unsigned_output, len); 19 | 20 | always @* begin 21 | for(i=0; i<=MB-1; i=i+1) begin 22 | gl [i ] = in[i*8 ]; // Glue bits 23 | out[i*7 +: 7] = in[i*8+1 +: 7]; // Data chunks 24 | end 25 | 26 | // Used bytes 27 | ub[0] = 1; 28 | 29 | for(i=1; i<=MB-1; i=i+1) 30 | ub[i] = gl[i-1] & ub[i-1]; // Should use more optimus &gl[i-1:0] instead 31 | 32 | // Get high order byte 33 | for(i=0; i<=MB-2; i=i+1) 34 | ho[i] = !ub[i+1] & ub[i]; 35 | 36 | ho[MB-1] = ub[MB-1]; 37 | 38 | // Get sign bit from high order byte 39 | for(i=0; i<=MB-1; i=i+1) 40 | sb[i] = ho[i] & in[i*8+1]; 41 | 42 | // If number is negative, extend sign to left 43 | if(|sb) begin 44 | for(i=0; i<=MB-1; i=i+1) 45 | if(!ub[i]) 46 | out[i*7 +: 7] = 7'b1111111; 47 | end 48 | 49 | // Number is positive 50 | else 51 | out = unsigned_output; 52 | end 53 | 54 | endmodule 55 | -------------------------------------------------------------------------------- /unpack_unsigned.v: -------------------------------------------------------------------------------- 1 | module unpack_unsigned #( 2 | parameter N = 64 3 | ) 4 | ( 5 | input [ 0:M-1] in, 6 | output reg [ N-1: 0] out, 7 | output reg [$clog2(MB)-1: 0] len 8 | ); 9 | 10 | localparam MB = N/7+1; 11 | localparam M = MB*8; 12 | 13 | integer i; 14 | reg [MB-1:0] gl, ub, ho; 15 | 16 | always @* begin 17 | for(i=0; i<=MB-1; i=i+1) begin 18 | gl [i ] = in[i*8 ]; // Glue bits 19 | out[i*7 +: 7] = in[i*8+1 +: 7]; // Data chunks 20 | end 21 | 22 | // Used bytes 23 | ub[0] = 1; 24 | 25 | for(i=1; i<=MB-1; i=i+1) 26 | ub[i] = gl[i-1] & ub[i-1]; // Should use more optimus &gl[i-1:0] instead 27 | 28 | // Get high order byte 29 | for(i=0; i<=MB-2; i=i+1) 30 | ho[i] = !ub[i+1] & ub[i]; 31 | 32 | ho[MB-1] = ub[MB-1]; 33 | 34 | // Generate decoded number 35 | for(i=0; i<=MB-1; i=i+1) 36 | if(!ub[i]) 37 | out[i*7 +: 7] = 7'b0; 38 | 39 | // Positional to binary 40 | len[0] = |(ho & 32'b01010101010101010101010101010101); 41 | if(N > 7) len[1] = |(ho & 32'b01100110011001100110011001100110); 42 | if(N > 21) len[2] = |(ho & 32'b01111000011110000111100001111000); 43 | if(N > 49) len[3] = |(ho & 32'b01111111100000000111111110000000); 44 | if(N > 105) len[4] = |(ho & 32'b01111111111111111000000000000000); 45 | end 46 | 47 | endmodule 48 | --------------------------------------------------------------------------------