Skip to content

Numeric Tools

Hardware Related Numeric Calculations.

Classes:

Name Description
AlignError

Alignment Error.

Functions:

Name Description
unsigned_to_hex

Convert unsigned value to width bit hex.

signed_to_hex

Convert signed value to width bit hex.

signed_to_unsigned

Convert signed value to unsigned with width.

unsigned_to_signed

Convert unsigned value to signed with width.

calc_unsigned_width

Return width in bits for value.

calc_signed_width

Return width in bits for value.

calc_lowest_bit_set

Return bit number which is not zero.

is_power_of2

Return True if value is power of 2.

is_power_of

Return True if value is power of base.

calc_next_power_of2

Return next power of 2.

calc_next_power_of

Return next power of base.

calc_prev_power_of2

Return previous power of base.

calc_prev_power_of

Return previous power of base.

align

Forward value to offset and align and minalign.

bytes2words

Convert list of bytes to list of words.

bytes2word

Convert list of bytes to one word.

words2bytes

Convert list of words to list of bytes.

convwidth

Convert iterable with values of srcwidth to list of values of destwidth.

to_mask

Convert address range starting at baseaddr with 2**exp elements to mask.

calc_addrwinmasks

Return tuple of binary compare wildcard masks for address window of size starting at baseaddr.

split_aligned_segs

Split address window starting at baseaddr with size into segments with aligned base addresses.

AlignError

Bases: RuntimeError

Alignment Error.

unsigned_to_hex

unsigned_to_hex(value, width, prefix='')

Convert unsigned value to width bit hex.

Example:

>>> unsigned_to_hex(166, 8)
'A6'
>>> unsigned_to_hex(3, 6)
'03'
>>> unsigned_to_hex(5, 9)
'005'
>>> unsigned_to_hex(26000000, 32)
'018CBA80'
>>> unsigned_to_hex(16, 4)
Traceback (most recent call last):
    ...
ValueError: 16 is not a unsigned 4 bit integer
>>> unsigned_to_hex(-1, 4)
Traceback (most recent call last):
    ...
ValueError: -1 is not a unsigned 4 bit integer
>>> unsigned_to_hex(26000000, 32, prefix="{width}'h")
"32'h018CBA80"
>>> unsigned_to_hex(248, 8, prefix="0x")
'0xF8'

signed_to_hex

signed_to_hex(value, width, prefix='')

Convert signed value to width bit hex.

Example:

>>> signed_to_hex(15, 8)
'0F'
>>> signed_to_hex(-3, 6)
'3D'
>>> signed_to_hex(5, 9)
'005'
>>> signed_to_hex(-9, 4)
Traceback (most recent call last):
    ...
ValueError: -9 is not a signed 4 bit integer
>>> signed_to_hex(8, 4)
Traceback (most recent call last):
    ...
ValueError: 8 is not a signed 4 bit integer
>>> signed_to_hex(5, 9, prefix="{width}'h")
"9'h005"

signed_to_unsigned

signed_to_unsigned(value, width)

Convert signed value to unsigned with width.

Example:

>>> signed_to_unsigned(3, 4)
3
>>> signed_to_unsigned(-3, 4)
13
>>> signed_to_unsigned(-3, 8)
253
>>> signed_to_unsigned(-9, 4)
Traceback (most recent call last):
    ...
ValueError: -9 is not a signed 4 bit integer
>>> signed_to_unsigned(8, 4)
Traceback (most recent call last):
    ...
ValueError: 8 is not a signed 4 bit integer

unsigned_to_signed

unsigned_to_signed(value, width)

Convert unsigned value to signed with width.

Example:

>>> unsigned_to_signed(3, 4)
3
>>> unsigned_to_signed(13, 4)
-3
>>> unsigned_to_signed(253, 8)
-3
>>> unsigned_to_signed(-9, 4)
Traceback (most recent call last):
    ...
ValueError: -9 is not a unsigned 4 bit integer

calc_unsigned_width

calc_unsigned_width(value)

Return width in bits for value.

>>> calc_unsigned_width(3)
2
>>> calc_unsigned_width(4)
3
>>> calc_unsigned_width(7)
3
>>> calc_unsigned_width(8)
4
>>> calc_unsigned_width(15)
4
>>> calc_unsigned_width(16)
5
>>> calc_unsigned_width(8191)
13
>>> calc_unsigned_width(0)
1
>>> calc_unsigned_width(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be not negative. -5 is not.

calc_signed_width

calc_signed_width(value)

Return width in bits for value.

>>> calc_signed_width(15)
5
>>> calc_signed_width(16)
6
>>> calc_signed_width(8191)
14
>>> calc_signed_width(-7)
4
>>> calc_signed_width(-8)
4
>>> calc_signed_width(-9)
5
>>> calc_signed_width(0)
1

calc_lowest_bit_set

calc_lowest_bit_set(num)

Return bit number which is not zero.

Example:

>>> calc_lowest_bit_set(0xE0)
5
>>> calc_lowest_bit_set(-8)
3
>>> calc_lowest_bit_set(0)

is_power_of2

is_power_of2(value)

Return True if value is power of 2.

>>> is_power_of2(15)
False
>>> is_power_of2(16)
True
>>> is_power_of2(17)
False
>>> is_power_of2(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be larger than zero. -5 is not.
>>> is_power_of2(0)
False

is_power_of

is_power_of(value, base=2)

Return True if value is power of base.

>>> is_power_of(15)
False
>>> is_power_of(16)
True
>>> is_power_of(17)
False
>>> is_power_of(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be larger than zero. -5 is not.

>>> is_power_of(8, base=3)
False
>>> is_power_of(9, base=3)
True
>>> is_power_of(-9, base=3)
Traceback (most recent call last):
    ...
AssertionError: Value must be larger than zero. -9 is not.
>>> is_power_of(0)
False
>>> is_power_of(0, base=6)
False

calc_next_power_of2

calc_next_power_of2(value)

Return next power of 2.

The returned value fulfills the rule 2**ceil(log2(value)) >= value.

>>> calc_next_power_of2(1)
2
>>> calc_next_power_of2(10)
16
>>> calc_next_power_of2(16)
16
>>> calc_next_power_of2(17)
32
>>> calc_next_power_of2(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be positive. -5 is not.

calc_next_power_of

calc_next_power_of(value, base=2)

Return next power of base.

The returned value fulfills the rule base**ceil(log_base(value)) >= value.

>>> calc_next_power_of(1)
2
>>> calc_next_power_of(1, base=5)
5
>>> calc_next_power_of(10)
16
>>> calc_next_power_of(10, base=3)
27
>>> calc_next_power_of(16)
16
>>> calc_next_power_of(9, base=3)
9
>>> calc_next_power_of(17)
32
>>> calc_next_power_of(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be positive. -5 is not.

calc_prev_power_of2

calc_prev_power_of2(value)

Return previous power of base.

The returned value fulfills the rule 2**floor(log2(value)) <= value.

>>> calc_prev_power_of2(1)
1
>>> calc_prev_power_of2(15)
8
>>> calc_prev_power_of2(16)
16
>>> calc_prev_power_of2(17)
16
>>> calc_prev_power_of2(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be positive. -5 is not.

calc_prev_power_of

calc_prev_power_of(value, base=2)

Return previous power of base.

The returned value fulfills the rule base**exp <= value.

>>> calc_prev_power_of(1)
1
>>> calc_prev_power_of(1, base=7)
1
>>> calc_prev_power_of(15)
8
>>> calc_prev_power_of(16)
16
>>> calc_prev_power_of(17)
16
>>> calc_prev_power_of(10, base=3)
9
>>> calc_prev_power_of(27, base=3)
27
>>> calc_prev_power_of(-5)
Traceback (most recent call last):
    ...
AssertionError: Value must be positive. -5 is not.

align

align(
    value, offset=None, align=None, minalign=1, rewind=False
)

Forward value to offset and align and minalign.

Without offset and align nothing happens.

>>> align(5)
5
>>> align(7)
7

An offset forwards the count if necessary or raises an :any:AlignError.

>>> align(5, offset=8)
8
>>> align(5, offset=3)
Traceback (most recent call last):
    ...
icdutil.num.AlignError: Cannot use offset 3 as we are already at 5

A align forwards the value to the next multiple of align.

>>> align(5, align=4)
8
>>> align(8, align=4)
8
>>> align(9, align=4)
12

A minalign without align forwards the value to the next multiple of minalign

>>> align(5, minalign=4)
8
>>> align(8, minalign=4)
8
>>> align(9, minalign=4)
12

If both align and minalign are given, then the value is moved to the next multiple of whichever of the both align values is bigger

>>> align(8, align=5, minalign=4)
10
>>> align(8, align=4, minalign=6)
12

If offset is given it is dominant over both align and minalign

>>> align(8, offset=9, align=4, minalign=6)
9

bytes2words

bytes2words(bytes_, bytesperword=4)

Convert list of bytes to list of words.

>>> for word in bytes2words([0x11, 0x22, 0x33, 0x44, 0x55, 0x66]):
...    print("0x%X" % word)
0x44332211
0x6655
>>> for word in bytes2words(bytes.fromhex("112233445566")):
...    print("0x%X" % word)
0x44332211
0x6655

>>> for word in bytes2words([0x11, 0x22, 0x33, 0x44, 0x55, 0x66], bytesperword=2):
...    print("0x%X" % word)
0x2211
0x4433
0x6655

>>> for word in bytes2words([]):
...    print("0x%X" % word)

bytes2word

bytes2word(bytes_)

Convert list of bytes to one word.

>>> print("0x%X" % bytes2word([0x11, 0x22, 0x33, 0x44, 0x55, 0x66]))
0x665544332211

words2bytes

words2bytes(words, bytesperword=4)

Convert list of words to list of bytes.

>>> for byte_ in words2bytes([0x44332211, 0x6655]):
...    print("0x%X" % byte_)
0x11
0x22
0x33
0x44
0x55
0x66
0x0
0x0

>>> for byte_ in words2bytes([0x44332211, 0x6655], bytesperword=6):
...    print("0x%X" % byte_)
0x11
0x22
0x33
0x44
0x0
0x0
0x55
0x66
0x0
0x0
0x0
0x0

convwidth

convwidth(iterable, srcwidth=32, destwidth=8)

Convert iterable with values of srcwidth to list of values of destwidth.

>>> [hex(i) for i in convwidth([0x04030201, 0x08070605], 32, 16)]
['0x201', '0x403', '0x605', '0x807']
>>> [hex(i) for i in convwidth([0x0807060504030201], 64, 16)]
['0x201', '0x403', '0x605', '0x807']
>>> [hex(i) for i in convwidth([0x0807060504030201], 64, 8)]
['0x1', '0x2', '0x3', '0x4', '0x5', '0x6', '0x7', '0x8']
>>> [hex(i) for i in convwidth([], 32, 16)]
[]

>>> [hex(i) for i in convwidth([0x201, 0x403, 0x605, 0x807], 16, 32)]
['0x4030201', '0x8070605']
>>> [hex(i) for i in convwidth([0x201, 0x403, 0x605, 0x807], 16, 64)]
['0x807060504030201']
>>> [hex(i) for i in convwidth([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8], 8, 64)]
['0x807060504030201']
>>> [hex(i) for i in convwidth([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8], 8, 56)]
['0x7060504030201', '0x8']
>>> [hex(i) for i in convwidth([], 16, 32)]
[]

>>> [hex(i) for i in convwidth([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8], 8, 8)]
['0x1', '0x2', '0x3', '0x4', '0x5', '0x6', '0x7', '0x8']

to_mask

to_mask(addrwidth, baseaddr, exp, dontcare='?')

Convert address range starting at baseaddr with 2**exp elements to mask.

>>> to_mask(16, 0x4000, 6)
'0100000000??????'
>>> to_mask(16, 0x6000, 4, 'x')
'011000000000xxxx'
>>> to_mask(16, 0x4004, 6)
Traceback (most recent call last):
    ...
AssertionError: 6 LSBs of '0100000000000100' shall be '000000', not '000100'

calc_addrwinmasks

calc_addrwinmasks(
    baseaddr, size, addrwidth=32, dontcare="?"
)

Return tuple of binary compare wildcard masks for address window of size starting at baseaddr.

Parameters:

Name Type Description Default
baseaddr int

Address window start address

required
size int

Address window size

required

Other Parameters:

Name Type Description
addrwidth int

Address width in bits

dontcare str

Character used for don't care bits

Example:

>>> calc_addrwinmasks(0, 1, addrwidth=4)
('0000',)
>>> calc_addrwinmasks(0xF000, 0x10, addrwidth=16)
('111100000000????',)
>>> calc_addrwinmasks(0xF000, 0x180, addrwidth=16, dontcare='x')
('11110000xxxxxxxx', '111100010xxxxxxx')
>>> calc_addrwinmasks(0xEFF0, 0x100, addrwidth=16)
('111011111111????', '111100000???????', '1111000010??????', '11110000110?????', '111100001110????')

split_aligned_segs

split_aligned_segs(baseaddr, size)

Split address window starting at baseaddr with size into segments with aligned base addresses.

The base addresses of the segments are aligned to calc_next_power_of2(size).

Returns tuple of :any:AddrRange. Segment starting at baseaddr with size.

Parameters:

Name Type Description Default
baseaddr int

Address window start address

required
size int

Address window size

required

Example:

>>> split_aligned_segs(0, 1)
(AddrRange(0x0, '1 byte'),)
>>> split_aligned_segs(1024, 16)
(AddrRange(0x400, '16 bytes'),)
>>> split_aligned_segs(1024, 1024)
(AddrRange(0x400, '1 KB'),)
>>> split_aligned_segs(1024, 1024+768)
(AddrRange(0x400, '1 KB'), AddrRange(0x800, '768 bytes'))
>>> split_aligned_segs(256, 1024+768)
(AddrRange(0x100, '256 bytes'), AddrRange(0x200, '512 bytes'), AddrRange(0x400, '1 KB'))
>>> split_aligned_segs(1000, 1024)  # doctest: +ELLIPSIS
(AddrRange(0x3E8, '8 bytes'), AddrRange(0x3F0, '16 bytes'), AddrRange(0x400, '512 b...(0x600, '488 bytes'))