blob: 130c07e6f79732e9842d7fadb743240be4e3ae26 [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
#include "bcmos_system.h"
#include "math.h"
#include "bcmolt_bit_utils.h"
/** dynamically allocate space for an array of `nbits' bits and initalize
* the bits to all be zero.
*
* \param[out] bv pointer to a bit_vector struct.
* \param[in] nbits number of bits to allocate.
* \return FALSE if space was not available, otherwise TRUE.
*/
bcmos_bool bcmolt_bv_new(bit_vector *bv, const uint32_t nbits)
{
uint32_t nwords = BCMOS_DIVIDE_ROUND_UP(nbits, (BITS_SZ));
bv->nbits = nbits;
bv->vector = (bv_bits *)calloc(nwords, sizeof(bv_bits));
return (bv->vector != NULL);
}
/** return the value of the `offset'th bit element of the bit vector.
*
* \param[in] bv pointer to a bit_vector struct.
* \param[in] offset offset of bit to test.
* \return FALSE if the bit offset is out of range, otherwise TRUE.
*/
bcmos_bool bcmolt_bv_get(const bit_vector *bv, const uint32_t offset)
{
bcmos_bool rv = BCMOS_FALSE;
if (offset <= bv->nbits)
{
rv = test_bits_set(bv->vector[(offset / BITS_SZ)],
(1 << (offset % BITS_SZ)));
}
else
{
BCMOS_TRACE_ERR("out of range %u\n", offset);
}
return rv;
}
/** set or clear the bit in position `offset' of the bit vector.
* bv->vector[bit_pos] is to be set (assigned to 1) if value is TRUE,
* otherwise it is to be cleared (assigned to 0).
*
* \param[in] bv pointer to a bit_vector struct.
* \param[in] offset offset of bit to set or clear.
* \param[in] value boolean value. TRUE for set, BCMOS_FALSE for clear.
* \return FALSE if the bit offset is out of range, otherwise TRUE.
*/
void bcmolt_bv_assign(bit_vector *bv,
const uint32_t offset,
const bcmos_bool value)
{
if (offset <= bv->nbits)
{
if (value)
{
bv->vector[offset / BITS_SZ] |= (1 << (offset % BITS_SZ));
}
else
{
bv->vector[offset / BITS_SZ] &= ~(1 << (offset % BITS_SZ));
}
}
else
{
BCMOS_TRACE_ERR("out of range %u\n", offset);
}
}
/** toggle the bit in position `offset' of the bit vector.
* i.e. if it was 1 it is 0; if it was 0 it is 1.
*
* \param[in] bv pointer to a bit_vector struct.
* \param[in] offset offset of bit to toggle.
* \return FALSE if the bit offset is out of range, otherwise TRUE.
*/
void bcmolt_bv_toggle(bit_vector *bv, const uint32_t offset)
{
if (offset <= bv->nbits)
{
bv->vector[offset / BITS_SZ] ^= (1 << (offset % BITS_SZ));
}
else
{
BCMOS_TRACE_ERR("out of range %u\n", offset);
}
}
/** copy bit vector from 'src' to 'dst'.
*
* \param[out] dst pointer to a bit_vector struct to copy to.
* \param[in] src pointer to a bit_vector struct to copy from.
* \param[in] nbits number of bits to copy.
* \return none.
*/
void bcmolt_bv_copy(bit_vector *dst,
const bit_vector *src,
const uint32_t nbits)
{
uint32_t i;
uint32_t nwords = nbits / BITS_SZ;
bv_bits bit_remainder = nbits % BITS_SZ;
if ((nbits <= dst->nbits) && (nbits <= src->nbits))
{
for (i = 0; i < nwords; i++)
{
dst->vector[i] = src->vector[i];
}
if (0 != bit_remainder)
{
dst->vector[nwords] = (dst->vector[nwords] & ~((2^bit_remainder) - 1)) |
(src->vector[nwords] & ((2^bit_remainder) - 1));
}
}
else
{
BCMOS_TRACE_ERR("out of range %u\n", nbits);
}
}
/** Print bit pattern of word FORMATTED to string.
*
* \param[in] value value to transform to bit string.
* \param[in] bitcnt count of bits to be shown.
* \param[out] outstr pointer to a output buffer to store the string.
* \return none
* \warning this fn doesn't check the size of 'outstr'.
* the caller should ensure 'outstr' has enough room for the
* bit string, space characters and null terminator.
*/
void bcmolt_bit_string(const bv_bits value, const uint32_t bitcnt, char *outstr)
{
uint32_t offset;
if (bitcnt <= BITS_SZ)
{
for (offset = 0; offset < bitcnt; offset++)
{
*outstr++ = ((value >> offset) & 1) + '0';
if ((((offset + 1) % 4) == 0))
{
*outstr++ = ' ';
}
}
}
*outstr = '\0';
}
/** Print all bits in the bit vector.
*
* \param[in] bv pointer to a bit_vector struct.
* \return none.
*/
void bcmolt_bv_dump(const bit_vector *bv)
{
uint32_t idx; /* word idx */
char outstr[BITS_SZ + 8 + 1]; /* 8 spaces + null */
for (idx = 0; idx < (bv->nbits / BITS_SZ); idx++)
{
bcmolt_bit_string(bv->vector[idx], BITS_SZ, outstr);
bcmos_printf("%s\n", outstr);
}
if (0 != (bv->nbits % BITS_SZ))
{
bcmolt_bit_string(bv->vector[idx], (bv->nbits % BITS_SZ), outstr);
bcmos_printf("%s\n", outstr);
}
}
/** Count the number of bits set in a long integer.
*
* \param[in] num integer value.
* \return number of bits set.
*/
uint32_t bcmolt_bit_count(uint32_t num)
{
num = ((num & 0xAAAAAAAAL) >> 1) + (num & 0x55555555L);
num = ((num & 0xCCCCCCCCL) >> 2) + (num & 0x33333333L);
num = ((num & 0xF0F0F0F0L) >> 4) + (num & 0x0F0F0F0FL);
num = ((num & 0xFF00FF00L) >> 8) + (num & 0x00FF00FFL);
num = ((num & 0xFFFF0000L) >> 16) + (num & 0x0000FFFFL);
return num;
}
/** Count the number of bits set in the whole bit vector.
*
* \param[in] bv pointer to the bit vector struct.
* \return number of bits set.
*/
uint32_t bcmolt_bv_bit_count(const bit_vector *bv)
{
uint32_t nwords = BCMOS_DIVIDE_ROUND_UP(bv->nbits, (BITS_SZ));
uint32_t cnt = 0;
while (0 != nwords--)
{
cnt += bcmolt_bit_count(bv->vector[nwords]);
}
return cnt;
}
/** Copy bit range from a 32-bit word array to an arbitrary bit position of
* the destination 32-bit word array.
*
* \param[in] dst destination buffer
* \param[in] dst_bytes destination buffer size in bytes
* \param[in] dst_bit_pos least bit position to dest
* \param[in] src source buffer
* \param[in] n_bits how many bits to copy from source
* \note src is in little endian and dst is in big endian.
*/
void bcmos_bit_range_set(uint32_t *dst, uint32_t dst_bytes, uint16_t dst_bit_pos,
uint32_t *src, uint16_t n_bits)
{
uint16_t bp = dst_bit_pos;
uint16_t len;
uint32_t wp;
uint32_t mask;
uint32_t src_idx;
uint32_t dst_idx;
wp = bp / 32;
bp = bp & (32 - 1);
src_idx = 0;
for (len = n_bits; len > 0; len -= 32)
{
if (bp != 0)
{
mask = (len < 32) ? (1 << len) - 1 : 0xFFFFFFFF;
dst_idx = wp;
dst[dst_idx] &= ~(mask << bp);
dst[dst_idx] |= src[src_idx] << bp;
wp++;
if (len > (32 - bp))
{
dst_idx = wp;
dst[dst_idx] &= ~(mask >> (32 - bp));
dst[dst_idx] |= src[src_idx] >> (32 - bp) & ((1 << bp) - 1);
}
}
else
{
dst_idx = wp;
if (len < 32)
{
mask = (1 << len) - 1;
dst[dst_idx] &= ~mask;
dst[dst_idx] |= src[src_idx] << bp;
}
else
{
dst[dst_idx] = src[src_idx];
}
wp++;
}
src_idx++;
}
}
/** Get bit range at an arbitrary bit position of a 32-bit word array
*
* \param[in] src source buffer (e.g. dataport)
* \param[in] src_bytes source buffer size in bytes
* \param[in] src_bit_pos least bit position of the source
* \param[in] dst destination buffer to store the bit value
* \param[in] n_bits how many bits to copy from srouce
* \note src is in big endian and dst is in little endian.
*/
void bcmos_bit_range_get(const uint32_t *src, uint32_t src_bytes,
uint16_t src_bit_pos, uint32_t *dst, uint16_t n_bits)
{
uint16_t bp = src_bit_pos; /* for readability */
uint16_t len = n_bits;
uint32_t wp;
uint32_t src_idx;
uint32_t dst_idx;
if (n_bits == 1)
{
wp = bp / 32;
bp = bp & (32 - 1);
src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp;
dst[0] = ((src[src_idx] & (1 << bp)) != 0) ? 1: 0;
return;
}
wp = bp / 32;
bp = bp & (32 - 1);
dst_idx = 0;
for (; len > 0; len -= 32)
{
if (bp != 0)
{
src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp;
dst[dst_idx] = src[src_idx] >> bp & ((1 << (32 - bp)) - 1);
wp++;
if (len > (32 - bp))
{
src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp;
dst[dst_idx] |= src[src_idx] << (32 - bp);
}
}
else
{
src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp;
dst[dst_idx] = src[src_idx];
wp++;
}
if (len < 32)
{
dst[dst_idx] &= ((1 << len) - 1);
}
dst_idx++;
}
}