#!/usr/bin/python
# Filename: bitstream.py
#
# Input: 64 bit per line raw bitstream, first bit at lsb.
# Output: 66 bit per line raw bitstream, first bit at lsb.
#
# Author: HanW
# Date: Nov/23/2011
#
from bitstring import *

#
# Helper function --
# Input: 64-bit per line, in hex, first bit as LSB
# Output: 66-bit per line, in binary, first bit at LSB
#
def conv_64_to_66_bin(file_in, file_out):
    print ('Convert 64-bit input stream to 66-bit output stream\n')
    # convert to binary stream
    fin = open(file_in, 'r')
    ftmp = open('/tmp/conv_64_to_66.bin', 'w')
    for line in fin:
        str_bin = BitString(uint=int(line,16), length=64)
        str_bin.reverse()
        ftmp.write(str_bin.bin[2:])
    ftmp.write('\n')
    fin.close()
    ftmp.close()
    
    # get bitstream
    fread = open('/tmp/conv_64_to_66.bin', 'r')
    data_bits = fread.read()
    
    # chop to 66 bit segments
    fbin = open(file_out, 'w')
    tot_l = len(data_bits) / 66
    for n in range(tot_l):
        str_bin = BitString(uint=int(data_bits[n*66:(n+1)*66],2), length=66)
        str_bin.reverse()
        fbin.write(str_bin.bin[2:])
        fbin.write('\n')
    fbin.close()
    fread.close()

#
# Input: 64-bit per line, in hex, first bit at LSB
# Output: 66-bit per line, in hex, first bit at LSB
#
def conv_64_to_66(file_in, file_out, spacing=1):
    # convert to binary output with 66-bit per line
    conv_64_to_66_bin(file_in, '/tmp/conv_64_to_66.66b')

    # convert back to hex
    fout = open(file_out, 'w')
    fbin = open('/tmp/conv_64_to_66.66b', 'r')
    for line in fbin:
        hbits = '%0x' % (int(line[0:2], 2))
        fout.write(hbits)
        if (spacing):
            fout.write(' ')
        lbits = '%016x' % (int(line[2:66],2))
        fout.write(lbits)
        fout.write('\n')
    fbin.close()
    fout.close()
    print ('Done!\n')

#
# Input: 64-bit per line, in hex, first bit at LSB
# Output: 128-bit per line, in hex, first bit at LSB
#
def conv_64_to_128(file_in, file_out):
    # combine two 64-bit line to one 128-bit line
    f64 = open(file_in, 'r')
    f128 = open(file_out, 'w')
    lines = sum(1 for line in f64)
    f64.seek(0)

    for n in range(lines / 2):
        data0 = f64.readline()
        data1 = f64.readline()
        f128.write(data1[0:len(data1)-1])
        f128.write(data0)
    f64.close()
    f128.close()

#
# Input: 64-bit per line, in hex, first bit at LSB
# Output: 40-bit per line, in hex, first bit at LSB
#
def conv_64_to_40(file_in, file_out):
    print ('Convert 64-bit input to 40-bit output stream\n')

    conv_64_to_128(file_in, '/tmp/conv_64_to_128.tmp')

    # convert to 40 bits
    f40 = open(file_out, 'w')
    f128 = open('/tmp/conv_64_to_128.tmp', 'r')
    lines = sum(1 for line in f128)
    f128.seek(0)

    for n in range(lines / 2):
        data0 = f128.readline()
        data1 = f128.readline()
        data2 = f128.readline()
        data3 = f128.readline()
        data4 = f128.readline()
        data_sum = data4[0:len(data4)-1] + data3[0:len(data3)-1] + data2[0:len(data2)-1] + data1[0:len(data1)-1] + data0
        for m in range (len(data_sum)/10):
            f40.write(data_sum[len(data_sum) - 1 - (m+1) * 10 : len(data_sum) - 1 - m * 10])
            f40.write('\n')
    f128.close()
    f40.close()
    print('Done!\n')

#
# Input: 64-bit per line, in hex, first bit at LSB
# Output: 66 bit per line, conform to our page format.
#
def conv_64_to_page(file_in, file_out):
    print ('Convert 64 to page formatted input\n')
    tmp1 = '/tmp/conv_to_page_1.tmp'
    tmp2 = '/tmp/conv_to_page_2.tmp'

    # Convert 64 to 66 binary
    conv_64_to_66_bin(file_in, tmp1)

    ftmp = open(tmp1, 'r')
    lines = sum(1 for line in ftmp)
    ftmp.seek(0)
    
    fpage = open(tmp2, 'w')

    for n in range (lines/496):
        sync = BitString(uint=0, length=32).bin[2:]
        data = ""
        for m in range (496):
            line = ftmp.readline()
            sync = line[64:66] + sync
            if (len(sync) % 64 == 0):
                fpage.write(BitString(uint=int(sync,2),length=64).hex[2:])
                fpage.write('\n')
                sync=''
            data = data + '%016x' % int(line[0:64],2) + '\n'
        fpage.write(data)
    fpage.close()
    ftmp.close()
    
    # convert to 128-bit per line
    conv_64_to_128(tmp2, file_out)

#
# Input: page formatted data
# NOTE: use input generated by conv_64_to_page.
# Output: 66-bit per line.
#
def conv_page_to_66(file_in, file_out):
    print('Convert page formatted data to 66-bit per line\n')
    fpage = open(file_in, 'r')
    fout = open(file_out, 'w')

    lines = sum(1 for x in fpage)
    fpage.seek(0)
    
    for l in range (lines/256):
        sync = ''
        for n in range (8):
            line = fpage.readline()
            sync = line[0:len(line)-1] + sync
        
        syncstr = BitString(uint=int(sync,16), length = 128 * 8).bin[2:]
        for m in range (248):
            line = fpage.readline()
            datastr = BitStream('0x' + line).bin[2:]
            num2 = int(datastr[len(datastr)/2 : len(datastr)] + syncstr[992 - m*2 - 2: 992 - m*2],2)
            fout.write('%017x' % num2)
            fout.write('\n')
            num1 = int(datastr[0:len(datastr)/2] + syncstr[992 - m*2 - 4: 992 - m*2 - 2] , 2)
            fout.write('%017x' % num1)
            fout.write('\n')
    fpage.close()
    fout.close()

#
# Input: x-bits per line, in hex, first bit at LSB
# Output: bitstream, in binary, one bit per line.
# x is length 
# format is 16 for 0x, 2 for 0b, 10 for decimal
#
def conv_x_to_bits (file_in, file_out, x, fmt):
    ftmp = open(file_in, 'r')
    lines = sum(1 for line in ftmp)
    ftmp.seek(0)
    fbits = open(file_out, 'w')
    for n in range (lines):
        l = ftmp.readline()
        lbits = BitString(uint = int(l, fmt), length = x).bin[2:]
        for m in range (x):
            fbits.write(lbits[x-m-1] + '\n')
    ftmp.close()
    fbits.close()
    print('Done!\n')
 
def conv_bits_to_hex (file_in, file_out):
    ftmp = open(file_in, 'r')
    lines = sum(1 for line in ftmp)
    ftmp.seek(0)
    fbits = open(file_out, 'w')
    for n in range (lines):
        l = ftmp.readline()
        lbits = hex(int(l, 2))[2:]
        fbits.write(lbits + '\n')
    ftmp.close()
    fbits.close()
    print('Done!\n')

def conv_66_to_hex (file_in, file_out, length):
    ftmp = open(file_in, 'r')
    lines = sum(1 for line in ftmp)
    ftmp.seek(0)
    fbits = open(file_out, 'w')
    for n in range (lines):
        l = ftmp.readline()
        lbits = int(l, 2)
        fbits.write("%017x" % lbits + '\n')
    ftmp.close()
    fbits.close()
    print('Done!\n')

#
# Convert 64bit per line to 1bit per line
#
def conv_64_to_bitstream(file_in, file_out):
    print ('convert 64 to bit stream.\n')
    conv_x_to_bits(file_in, file_out, 64, 16)

def conv_32_to_bitstream(file_in, file_out):
    print ('convert 32 to bit stream.\n')
    conv_x_to_bits(file_in, file_out, 32, 16)

#
# Convert 40bit per line to 1bit per line
#    
def conv_40_to_bitstream(file_in, file_out):
    print ('Convert 40 to bit stream.\n')                              
    conv_x_to_bits(file_in, file_out, 40, 16)
#
# Convert 66bit per line to 1bit per line
#
def conv_66_to_bitstream(file_in, file_out):
    print ('convert 66 to bit stream.\n')
    conv_x_to_bits(file_in, file_out, 66, 16)

def conv_bin66_to_bitstream(file_in, file_out):
    print ('convert bin 66 to bitstream. \n')
    conv_x_to_bits(file_in, file_out, 66, 2)

def conv_bin40_to_bitstream(file_in, file_out):
    print ('convert bin 40 to bitstream. \n')
    conv_x_to_bits(file_in, file_out, 40, 2)

def conv_bin32_to_bitstream(file_in, file_out):
    print ('convert bin 32 to bitstream. \n')
    conv_x_to_bits(file_in, file_out, 32, 2)
#
# Convert from 128-bit per line page to 32 bit per line
# Used in Modelsim sonic_app simulation
#

def conv_page_to_32 (file_in, file_out):
    print ('convert page to 32 bit.\n')
    ftmp = open(file_in, 'r')
    lines = sum(1 for line in ftmp)
    ftmp.seek(0)
    fout = open(file_out, 'w')
    for n in range(lines):
        l = ftmp.readline()
        fout.write(l[24:32] + '\n')
        fout.write(l[16:24] + '\n')
        fout.write(l[8:16] + '\n')
        fout.write(l[0:8] + '\n')
    fout.close()
    ftmp.close()
