crypto: crct10dif - Accelerated CRC T10 DIF computation with PCLMULQDQ instruction
authorTim Chen <tim.c.chen@linux.intel.com>
Wed, 1 May 2013 19:52:49 +0000 (12:52 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Mon, 20 May 2013 12:11:06 +0000 (20:11 +0800)
This is the x86_64 CRC T10 DIF transform accelerated with the PCLMULQDQ
instructions.  Details discussing the implementation can be found in the
paper:

"Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction"
http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf

Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
arch/x86/crypto/crct10dif-pcl-asm_64.S [new file with mode: 0644]

diff --git a/arch/x86/crypto/crct10dif-pcl-asm_64.S b/arch/x86/crypto/crct10dif-pcl-asm_64.S
new file mode 100644 (file)
index 0000000..35e9756
--- /dev/null
@@ -0,0 +1,643 @@
+########################################################################
+# Implement fast CRC-T10DIF computation with SSE and PCLMULQDQ instructions
+#
+# Copyright (c) 2013, Intel Corporation
+#
+# Authors:
+#     Erdinc Ozturk <erdinc.ozturk@intel.com>
+#     Vinodh Gopal <vinodh.gopal@intel.com>
+#     James Guilford <james.guilford@intel.com>
+#     Tim Chen <tim.c.chen@linux.intel.com>
+#
+# This software is available to you under a choice of one of two
+# licenses.  You may choose to be licensed under the terms of the GNU
+# General Public License (GPL) Version 2, available from the file
+# COPYING in the main directory of this source tree, or the
+# OpenIB.org BSD license below:
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the
+#   distribution.
+#
+# * Neither the name of the Intel Corporation nor the names of its
+#   contributors may be used to endorse or promote products derived from
+#   this software without specific prior written permission.
+#
+#
+# THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+########################################################################
+#       Function API:
+#       UINT16 crc_t10dif_pcl(
+#               UINT16 init_crc, //initial CRC value, 16 bits
+#               const unsigned char *buf, //buffer pointer to calculate CRC on
+#               UINT64 len //buffer length in bytes (64-bit data)
+#       );
+#
+#       Reference paper titled "Fast CRC Computation for Generic
+#      Polynomials Using PCLMULQDQ Instruction"
+#       URL: http://www.intel.com/content/dam/www/public/us/en/documents
+#  /white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
+#
+#
+
+#include <linux/linkage.h>
+
+.text
+
+#define        arg1 %rdi
+#define        arg2 %rsi
+#define        arg3 %rdx
+
+#define        arg1_low32 %edi
+
+ENTRY(crc_t10dif_pcl)
+.align 16
+
+       # adjust the 16-bit initial_crc value, scale it to 32 bits
+       shl     $16, arg1_low32
+
+       # Allocate Stack Space
+       mov     %rsp, %rcx
+       sub     $16*2, %rsp
+       # align stack to 16 byte boundary
+       and     $~(0x10 - 1), %rsp
+
+       # check if smaller than 256
+       cmp     $256, arg3
+
+       # for sizes less than 128, we can't fold 64B at a time...
+       jl      _less_than_128
+
+
+       # load the initial crc value
+       movd    arg1_low32, %xmm10      # initial crc
+
+       # crc value does not need to be byte-reflected, but it needs
+       # to be moved to the high part of the register.
+       # because data will be byte-reflected and will align with
+       # initial crc at correct place.
+       pslldq  $12, %xmm10
+
+       movdqa  SHUF_MASK(%rip), %xmm11
+       # receive the initial 64B data, xor the initial crc value
+       movdqu  16*0(arg2), %xmm0
+       movdqu  16*1(arg2), %xmm1
+       movdqu  16*2(arg2), %xmm2
+       movdqu  16*3(arg2), %xmm3
+       movdqu  16*4(arg2), %xmm4
+       movdqu  16*5(arg2), %xmm5
+       movdqu  16*6(arg2), %xmm6
+       movdqu  16*7(arg2), %xmm7
+
+       pshufb  %xmm11, %xmm0
+       # XOR the initial_crc value
+       pxor    %xmm10, %xmm0
+       pshufb  %xmm11, %xmm1
+       pshufb  %xmm11, %xmm2
+       pshufb  %xmm11, %xmm3
+       pshufb  %xmm11, %xmm4
+       pshufb  %xmm11, %xmm5
+       pshufb  %xmm11, %xmm6
+       pshufb  %xmm11, %xmm7
+
+       movdqa  rk3(%rip), %xmm10       #xmm10 has rk3 and rk4
+                                       #imm value of pclmulqdq instruction
+                                       #will determine which constant to use
+
+       #################################################################
+       # we subtract 256 instead of 128 to save one instruction from the loop
+       sub     $256, arg3
+
+       # at this section of the code, there is 64*x+y (0<=y<64) bytes of
+       # buffer. The _fold_64_B_loop will fold 64B at a time
+       # until we have 64+y Bytes of buffer
+
+
+       # fold 64B at a time. This section of the code folds 4 xmm
+       # registers in parallel
+_fold_64_B_loop:
+
+       # update the buffer pointer
+       add     $128, arg2              #    buf += 64#
+
+       movdqu  16*0(arg2), %xmm9
+       movdqu  16*1(arg2), %xmm12
+       pshufb  %xmm11, %xmm9
+       pshufb  %xmm11, %xmm12
+       movdqa  %xmm0, %xmm8
+       movdqa  %xmm1, %xmm13
+       pclmulqdq       $0x0 , %xmm10, %xmm0
+       pclmulqdq       $0x11, %xmm10, %xmm8
+       pclmulqdq       $0x0 , %xmm10, %xmm1
+       pclmulqdq       $0x11, %xmm10, %xmm13
+       pxor    %xmm9 , %xmm0
+       xorps   %xmm8 , %xmm0
+       pxor    %xmm12, %xmm1
+       xorps   %xmm13, %xmm1
+
+       movdqu  16*2(arg2), %xmm9
+       movdqu  16*3(arg2), %xmm12
+       pshufb  %xmm11, %xmm9
+       pshufb  %xmm11, %xmm12
+       movdqa  %xmm2, %xmm8
+       movdqa  %xmm3, %xmm13
+       pclmulqdq       $0x0, %xmm10, %xmm2
+       pclmulqdq       $0x11, %xmm10, %xmm8
+       pclmulqdq       $0x0, %xmm10, %xmm3
+       pclmulqdq       $0x11, %xmm10, %xmm13
+       pxor    %xmm9 , %xmm2
+       xorps   %xmm8 , %xmm2
+       pxor    %xmm12, %xmm3
+       xorps   %xmm13, %xmm3
+
+       movdqu  16*4(arg2), %xmm9
+       movdqu  16*5(arg2), %xmm12
+       pshufb  %xmm11, %xmm9
+       pshufb  %xmm11, %xmm12
+       movdqa  %xmm4, %xmm8
+       movdqa  %xmm5, %xmm13
+       pclmulqdq       $0x0,  %xmm10, %xmm4
+       pclmulqdq       $0x11, %xmm10, %xmm8
+       pclmulqdq       $0x0,  %xmm10, %xmm5
+       pclmulqdq       $0x11, %xmm10, %xmm13
+       pxor    %xmm9 ,  %xmm4
+       xorps   %xmm8 ,  %xmm4
+       pxor    %xmm12,  %xmm5
+       xorps   %xmm13,  %xmm5
+
+       movdqu  16*6(arg2), %xmm9
+       movdqu  16*7(arg2), %xmm12
+       pshufb  %xmm11, %xmm9
+       pshufb  %xmm11, %xmm12
+       movdqa  %xmm6 , %xmm8
+       movdqa  %xmm7 , %xmm13
+       pclmulqdq       $0x0 , %xmm10, %xmm6
+       pclmulqdq       $0x11, %xmm10, %xmm8
+       pclmulqdq       $0x0 , %xmm10, %xmm7
+       pclmulqdq       $0x11, %xmm10, %xmm13
+       pxor    %xmm9 , %xmm6
+       xorps   %xmm8 , %xmm6
+       pxor    %xmm12, %xmm7
+       xorps   %xmm13, %xmm7
+
+       sub     $128, arg3
+
+       # check if there is another 64B in the buffer to be able to fold
+       jge     _fold_64_B_loop
+       ##################################################################
+
+
+       add     $128, arg2
+       # at this point, the buffer pointer is pointing at the last y Bytes
+       # of the buffer the 64B of folded data is in 4 of the xmm
+       # registers: xmm0, xmm1, xmm2, xmm3
+
+
+       # fold the 8 xmm registers to 1 xmm register with different constants
+
+       movdqa  rk9(%rip), %xmm10
+       movdqa  %xmm0, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm0
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       xorps   %xmm0, %xmm7
+
+       movdqa  rk11(%rip), %xmm10
+       movdqa  %xmm1, %xmm8
+       pclmulqdq        $0x11, %xmm10, %xmm1
+       pclmulqdq        $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       xorps   %xmm1, %xmm7
+
+       movdqa  rk13(%rip), %xmm10
+       movdqa  %xmm2, %xmm8
+       pclmulqdq        $0x11, %xmm10, %xmm2
+       pclmulqdq        $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       pxor    %xmm2, %xmm7
+
+       movdqa  rk15(%rip), %xmm10
+       movdqa  %xmm3, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm3
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       xorps   %xmm3, %xmm7
+
+       movdqa  rk17(%rip), %xmm10
+       movdqa  %xmm4, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm4
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       pxor    %xmm4, %xmm7
+
+       movdqa  rk19(%rip), %xmm10
+       movdqa  %xmm5, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm5
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       xorps   %xmm5, %xmm7
+
+       movdqa  rk1(%rip), %xmm10       #xmm10 has rk1 and rk2
+                                       #imm value of pclmulqdq instruction
+                                       #will determine which constant to use
+       movdqa  %xmm6, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm6
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       pxor    %xmm6, %xmm7
+
+
+       # instead of 64, we add 48 to the loop counter to save 1 instruction
+       # from the loop instead of a cmp instruction, we use the negative
+       # flag with the jl instruction
+       add     $128-16, arg3
+       jl      _final_reduction_for_128
+
+       # now we have 16+y bytes left to reduce. 16 Bytes is in register xmm7
+       # and the rest is in memory. We can fold 16 bytes at a time if y>=16
+       # continue folding 16B at a time
+
+_16B_reduction_loop:
+       movdqa  %xmm7, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm7
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       movdqu  (arg2), %xmm0
+       pshufb  %xmm11, %xmm0
+       pxor    %xmm0 , %xmm7
+       add     $16, arg2
+       sub     $16, arg3
+       # instead of a cmp instruction, we utilize the flags with the
+       # jge instruction equivalent of: cmp arg3, 16-16
+       # check if there is any more 16B in the buffer to be able to fold
+       jge     _16B_reduction_loop
+
+       #now we have 16+z bytes left to reduce, where 0<= z < 16.
+       #first, we reduce the data in the xmm7 register
+
+
+_final_reduction_for_128:
+       # check if any more data to fold. If not, compute the CRC of
+       # the final 128 bits
+       add     $16, arg3
+       je      _128_done
+
+       # here we are getting data that is less than 16 bytes.
+       # since we know that there was data before the pointer, we can
+       # offset the input pointer before the actual point, to receive
+       # exactly 16 bytes. after that the registers need to be adjusted.
+_get_last_two_xmms:
+       movdqa  %xmm7, %xmm2
+
+       movdqu  -16(arg2, arg3), %xmm1
+       pshufb  %xmm11, %xmm1
+
+       # get rid of the extra data that was loaded before
+       # load the shift constant
+       lea     pshufb_shf_table+16(%rip), %rax
+       sub     arg3, %rax
+       movdqu  (%rax), %xmm0
+
+       # shift xmm2 to the left by arg3 bytes
+       pshufb  %xmm0, %xmm2
+
+       # shift xmm7 to the right by 16-arg3 bytes
+       pxor    mask1(%rip), %xmm0
+       pshufb  %xmm0, %xmm7
+       pblendvb        %xmm2, %xmm1    #xmm0 is implicit
+
+       # fold 16 Bytes
+       movdqa  %xmm1, %xmm2
+       movdqa  %xmm7, %xmm8
+       pclmulqdq       $0x11, %xmm10, %xmm7
+       pclmulqdq       $0x0 , %xmm10, %xmm8
+       pxor    %xmm8, %xmm7
+       pxor    %xmm2, %xmm7
+
+_128_done:
+       # compute crc of a 128-bit value
+       movdqa  rk5(%rip), %xmm10       # rk5 and rk6 in xmm10
+       movdqa  %xmm7, %xmm0
+
+       #64b fold
+       pclmulqdq       $0x1, %xmm10, %xmm7
+       pslldq  $8   ,  %xmm0
+       pxor    %xmm0,  %xmm7
+
+       #32b fold
+       movdqa  %xmm7, %xmm0
+
+       pand    mask2(%rip), %xmm0
+
+       psrldq  $12, %xmm7
+       pclmulqdq       $0x10, %xmm10, %xmm7
+       pxor    %xmm0, %xmm7
+
+       #barrett reduction
+_barrett:
+       movdqa  rk7(%rip), %xmm10       # rk7 and rk8 in xmm10
+       movdqa  %xmm7, %xmm0
+       pclmulqdq       $0x01, %xmm10, %xmm7
+       pslldq  $4, %xmm7
+       pclmulqdq       $0x11, %xmm10, %xmm7
+
+       pslldq  $4, %xmm7
+       pxor    %xmm0, %xmm7
+       pextrd  $1, %xmm7, %eax
+
+_cleanup:
+       # scale the result back to 16 bits
+       shr     $16, %eax
+       mov     %rcx, %rsp
+       ret
+
+########################################################################
+
+.align 16
+_less_than_128:
+
+       # check if there is enough buffer to be able to fold 16B at a time
+       cmp     $32, arg3
+       jl      _less_than_32
+       movdqa  SHUF_MASK(%rip), %xmm11
+
+       # now if there is, load the constants
+       movdqa  rk1(%rip), %xmm10       # rk1 and rk2 in xmm10
+
+       movd    arg1_low32, %xmm0       # get the initial crc value
+       pslldq  $12, %xmm0      # align it to its correct place
+       movdqu  (arg2), %xmm7   # load the plaintext
+       pshufb  %xmm11, %xmm7   # byte-reflect the plaintext
+       pxor    %xmm0, %xmm7
+
+
+       # update the buffer pointer
+       add     $16, arg2
+
+       # update the counter. subtract 32 instead of 16 to save one
+       # instruction from the loop
+       sub     $32, arg3
+
+       jmp     _16B_reduction_loop
+
+
+.align 16
+_less_than_32:
+       # mov initial crc to the return value. this is necessary for
+       # zero-length buffers.
+       mov     arg1_low32, %eax
+       test    arg3, arg3
+       je      _cleanup
+
+       movdqa  SHUF_MASK(%rip), %xmm11
+
+       movd    arg1_low32, %xmm0       # get the initial crc value
+       pslldq  $12, %xmm0      # align it to its correct place
+
+       cmp     $16, arg3
+       je      _exact_16_left
+       jl      _less_than_16_left
+
+       movdqu  (arg2), %xmm7   # load the plaintext
+       pshufb  %xmm11, %xmm7   # byte-reflect the plaintext
+       pxor    %xmm0 , %xmm7   # xor the initial crc value
+       add     $16, arg2
+       sub     $16, arg3
+       movdqa  rk1(%rip), %xmm10       # rk1 and rk2 in xmm10
+       jmp     _get_last_two_xmms
+
+
+.align 16
+_less_than_16_left:
+       # use stack space to load data less than 16 bytes, zero-out
+       # the 16B in memory first.
+
+       pxor    %xmm1, %xmm1
+       mov     %rsp, %r11
+       movdqa  %xmm1, (%r11)
+
+       cmp     $4, arg3
+       jl      _only_less_than_4
+
+       # backup the counter value
+       mov     arg3, %r9
+       cmp     $8, arg3
+       jl      _less_than_8_left
+
+       # load 8 Bytes
+       mov     (arg2), %rax
+       mov     %rax, (%r11)
+       add     $8, %r11
+       sub     $8, arg3
+       add     $8, arg2
+_less_than_8_left:
+
+       cmp     $4, arg3
+       jl      _less_than_4_left
+
+       # load 4 Bytes
+       mov     (arg2), %eax
+       mov     %eax, (%r11)
+       add     $4, %r11
+       sub     $4, arg3
+       add     $4, arg2
+_less_than_4_left:
+
+       cmp     $2, arg3
+       jl      _less_than_2_left
+
+       # load 2 Bytes
+       mov     (arg2), %ax
+       mov     %ax, (%r11)
+       add     $2, %r11
+       sub     $2, arg3
+       add     $2, arg2
+_less_than_2_left:
+       cmp     $1, arg3
+        jl      _zero_left
+
+       # load 1 Byte
+       mov     (arg2), %al
+       mov     %al, (%r11)
+_zero_left:
+       movdqa  (%rsp), %xmm7
+       pshufb  %xmm11, %xmm7
+       pxor    %xmm0 , %xmm7   # xor the initial crc value
+
+       # shl r9, 4
+       lea     pshufb_shf_table+16(%rip), %rax
+       sub     %r9, %rax
+       movdqu  (%rax), %xmm0
+       pxor    mask1(%rip), %xmm0
+
+       pshufb  %xmm0, %xmm7
+       jmp     _128_done
+
+.align 16
+_exact_16_left:
+       movdqu  (arg2), %xmm7
+       pshufb  %xmm11, %xmm7
+       pxor    %xmm0 , %xmm7   # xor the initial crc value
+
+       jmp     _128_done
+
+_only_less_than_4:
+       cmp     $3, arg3
+       jl      _only_less_than_3
+
+       # load 3 Bytes
+       mov     (arg2), %al
+       mov     %al, (%r11)
+
+       mov     1(arg2), %al
+       mov     %al, 1(%r11)
+
+       mov     2(arg2), %al
+       mov     %al, 2(%r11)
+
+       movdqa   (%rsp), %xmm7
+       pshufb   %xmm11, %xmm7
+       pxor     %xmm0 , %xmm7  # xor the initial crc value
+
+       psrldq  $5, %xmm7
+
+       jmp     _barrett
+_only_less_than_3:
+       cmp     $2, arg3
+       jl      _only_less_than_2
+
+       # load 2 Bytes
+       mov     (arg2), %al
+       mov     %al, (%r11)
+
+       mov     1(arg2), %al
+       mov     %al, 1(%r11)
+
+       movdqa  (%rsp), %xmm7
+       pshufb  %xmm11, %xmm7
+       pxor    %xmm0 , %xmm7   # xor the initial crc value
+
+       psrldq  $6, %xmm7
+
+       jmp     _barrett
+_only_less_than_2:
+
+       # load 1 Byte
+       mov     (arg2), %al
+       mov     %al, (%r11)
+
+       movdqa  (%rsp), %xmm7
+       pshufb  %xmm11, %xmm7
+       pxor    %xmm0 , %xmm7   # xor the initial crc value
+
+       psrldq  $7, %xmm7
+
+       jmp     _barrett
+
+ENDPROC(crc_t10dif_pcl)
+
+.data
+
+# precomputed constants
+# these constants are precomputed from the poly:
+# 0x8bb70000 (0x8bb7 scaled to 32 bits)
+.align 16
+# Q = 0x18BB70000
+# rk1 = 2^(32*3) mod Q << 32
+# rk2 = 2^(32*5) mod Q << 32
+# rk3 = 2^(32*15) mod Q << 32
+# rk4 = 2^(32*17) mod Q << 32
+# rk5 = 2^(32*3) mod Q << 32
+# rk6 = 2^(32*2) mod Q << 32
+# rk7 = floor(2^64/Q)
+# rk8 = Q
+rk1:
+.quad 0x2d56000000000000
+rk2:
+.quad 0x06df000000000000
+rk3:
+.quad 0x9d9d000000000000
+rk4:
+.quad 0x7cf5000000000000
+rk5:
+.quad 0x2d56000000000000
+rk6:
+.quad 0x1368000000000000
+rk7:
+.quad 0x00000001f65a57f8
+rk8:
+.quad 0x000000018bb70000
+
+rk9:
+.quad 0xceae000000000000
+rk10:
+.quad 0xbfd6000000000000
+rk11:
+.quad 0x1e16000000000000
+rk12:
+.quad 0x713c000000000000
+rk13:
+.quad 0xf7f9000000000000
+rk14:
+.quad 0x80a6000000000000
+rk15:
+.quad 0x044c000000000000
+rk16:
+.quad 0xe658000000000000
+rk17:
+.quad 0xad18000000000000
+rk18:
+.quad 0xa497000000000000
+rk19:
+.quad 0x6ee3000000000000
+rk20:
+.quad 0xe7b5000000000000
+
+
+
+mask1:
+.octa 0x80808080808080808080808080808080
+mask2:
+.octa 0x00000000FFFFFFFFFFFFFFFFFFFFFFFF
+
+SHUF_MASK:
+.octa 0x000102030405060708090A0B0C0D0E0F
+
+pshufb_shf_table:
+# use these values for shift constants for the pshufb instruction
+# different alignments result in values as shown:
+#      DDQ 0x008f8e8d8c8b8a898887868584838281 # shl 15 (16-1) / shr1
+#      DDQ 0x01008f8e8d8c8b8a8988878685848382 # shl 14 (16-3) / shr2
+#      DDQ 0x0201008f8e8d8c8b8a89888786858483 # shl 13 (16-4) / shr3
+#      DDQ 0x030201008f8e8d8c8b8a898887868584 # shl 12 (16-4) / shr4
+#      DDQ 0x04030201008f8e8d8c8b8a8988878685 # shl 11 (16-5) / shr5
+#      DDQ 0x0504030201008f8e8d8c8b8a89888786 # shl 10 (16-6) / shr6
+#      DDQ 0x060504030201008f8e8d8c8b8a898887 # shl 9  (16-7) / shr7
+#      DDQ 0x07060504030201008f8e8d8c8b8a8988 # shl 8  (16-8) / shr8
+#      DDQ 0x0807060504030201008f8e8d8c8b8a89 # shl 7  (16-9) / shr9
+#      DDQ 0x090807060504030201008f8e8d8c8b8a # shl 6  (16-10) / shr10
+#      DDQ 0x0a090807060504030201008f8e8d8c8b # shl 5  (16-11) / shr11
+#      DDQ 0x0b0a090807060504030201008f8e8d8c # shl 4  (16-12) / shr12
+#      DDQ 0x0c0b0a090807060504030201008f8e8d # shl 3  (16-13) / shr13
+#      DDQ 0x0d0c0b0a090807060504030201008f8e # shl 2  (16-14) / shr14
+#      DDQ 0x0e0d0c0b0a090807060504030201008f # shl 1  (16-15) / shr15
+.octa 0x8f8e8d8c8b8a89888786858483828100
+.octa 0x000e0d0c0b0a09080706050403020100