This is the first of a series of posts about useful patterns I use with Cocotb. It’s probably documented somewhere in the Cocotb documentation, but I am writing them here for my own reference.
Anyway, this one is about a simple but important topic: how to deal with double-words, bytes, bits and endianness. It’s simple but easy to get wrong, which can lead to hours of wasted debugging (well, I did waste hours on this once).
Starting with a list of DW of a packet of some sort, dw_to_bytes takes a DW and generates a list of bytes depending on endianness. bytes_to_signal takes a list of bytes and flattens them starting from byte 0 (the MSB in the original DW).
Now, we have a signal that can be assigned to cocotb’s .value. Note this works as the SystemVerilog data_in range is [1023:0] which is the norm I guess. If the range is reversed, that adds another gotcha.
import cocotb
from cocotb.triggers import Timer
def dw_to_bytes(dw, endian='little'):
b = []
if endian == 'big':
b = [(dw >> (8 * (3 - i))) & 0xFF for i in range(4)]
else:
b = [(dw >> (8 * i)) & 0xFF for i in range(4)]
return b
def bytes_to_signal(bytes):
sig = 0
for i in range(len(bytes)):
sig |= (bytes[i] << (8 * i))
return sig
@cocotb.test()
async def test_bytes(dut):
packet = [0x40AABBDD, 0x50000002]
bytes = []
for dw in packet:
bytes.extend(dw_to_bytes(dw, endian='big'))
print(f"Bytes: {[f'0x{b:02X}' for b in bytes]}")
dut.data_in.value = bytes_to_signal(bytes)
await Timer(10, unit='ns')
print(f"Data in: {hex(dut.data_in.value)}")
Bytes: ['0x40', '0xAA', '0xBB', '0xDD', '0x50', '0x00', '0x00', '0x02']
Data in: 0x2000050ddbbaa40
Bits Link to heading
The built-in types (int/bytes) can be tricky when accessing bits, as we have to use shift/mask operations and probably use bin or hex for printing.
dw0 = packet[0]
print(f'DW0: 0x{dw0:08X}')
print(f"bin(Bit[15:8]): {bin(dw0 >> 8 & 0xff)}") # bin() returns str
print(f"hex(Bit[15:8]): {hex(dw0 >> 8 & 0xff)}") # hex() returns str
DW0: 0x40AABBDD
bin(Bit[15:8]): 0b10111011
hex(Bit[15:8]): 0xbb
On the other hand, Cocotb’s value uses Logic and LogicArray which are indexable.
print(dut.data_in.value[7:0]) # Byte 0
print(type(dut.data_in.value[7:0]))
01000000
<class 'cocotb.types.LogicArray'>