alexforencich/cocotbext-axi

Axi stream receiving 8b chunks when sending 32b data

Closed this issue · 3 comments

Hi!

Before everything, I want to thank you for this fantastic cocotb extension. The axi-lite module helped test for testing my modules. Recently, I wanted to use the AXI stream module for digital filter design. I've checked the test directory, and based on that; I created a simple example of a system verilog module that has a slave and master bus with 32b data width:

`timescale 1ns / 1ns

module axis_if #
(
    parameter DATA_WIDTH = 32,
    parameter KEEP_WIDTH = (DATA_WIDTH/8)
)
(
    input  logic                  clk,
    input  logic                   rst,

    // Stream slave interface
    input  logic [DATA_WIDTH-1:0]  s_axis_tdata, // optional, needed for data
    input  logic                   s_axis_tvalid, // required
    output  logic                  s_axis_tready, // optional, highly recommended
    input  logic [KEEP_WIDTH-1:0]  s_axis_tkeep, // optional, needed for simulation
    // Stream master interface
    output  logic [DATA_WIDTH-1:0]  m_axis_tdata, // optional, needed for data
    output  logic                   m_axis_tvalid, // required
    input  logic                    m_axis_tready, // optional, highly recommended
    output  logic [KEEP_WIDTH-1:0]  m_axis_tkeep // optional, needed for simulation
);

initial begin
    $dumpfile("axis.vcd");
    $dumpvars;
end

    assign m_axis_tdata = s_axis_tdata;
    assign m_axis_tvalid = s_axis_tvalid;
    assign s_axis_tready = s_axis_tready;
    assign m_axis_tkeep = s_axis_tkeep;

endmodule

On the python side, I have an equally simple test of sending 3 x 32b data packets, and receiving them:

import os
from pathlib import Path

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge
from cocotb.runner import get_runner
from cocotbext.axi import AxiStreamFrame, AxiStreamBus, AxiStreamSource, AxiStreamSink, AxiStreamMonitor

class TB:
    def __init__(self, dut):
        self.dut = dut

        cocotb.start_soon(Clock(dut.clk, 2, units="ns").start())

        self.source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.clk, dut.rst)
        self.sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
        # self.monitor = AxiStreamMonitor(AxiStreamBus.from_prefix(dut, "axis"), dut.clk, dut.rst)

    async def reset(self):
        self.dut.rst.setimmediatevalue(0)
        await RisingEdge(self.dut.clk)
        await RisingEdge(self.dut.clk)
        self.dut.rst.value = 1
        await RisingEdge(self.dut.clk)
        await RisingEdge(self.dut.clk)
        self.dut.rst.value = 0
        await RisingEdge(self.dut.clk)
        await RisingEdge(self.dut.clk)

@cocotb.test()
async def run_test(dut):
    tb = TB(dut)
    await tb.reset()

    test_frames = [b'10101010101010101010101010101010', b'11001100110011001100110011001100', b'11110000111100001111000011110000']

    for frame in test_frames:
        tx_frame = AxiStreamFrame(frame)
        await tb.source.send(tx_frame)
        print(f"Sent frame {frame}")

    for frame in test_frames:
        rx_frame = await tb.sink.read()
        print(f"Received frame {rx_frame}")

    assert tb.sink.empty()
    await RisingEdge(dut.clk)
    await RisingEdge(dut.clk)
    
def test_axis_runner():
    hdl_toplevel_lang = os.getenv("HDL_TOPLEVEL_LANG", "verilog")
    sim = os.getenv("SIM", "icarus")

    proj_path = Path(__file__).resolve().parent
    verilog_sources = [proj_path / "axis_if.sv"]

    runner = get_runner(sim)
    runner.build(
        verilog_sources=verilog_sources,
        hdl_toplevel="axis_if",
        always=True,
    )

    runner.test(hdl_toplevel="axis_if", test_module="test_axis,")


if __name__ == "__main__":
    test_axis_runner()

With make file:

TOPLEVEL_LANG ?= verilog


VERILOG_SOURCES += $(PWD)/axis_if.sv


MODULE = test_axis
TOPLEVEL = axis_if

include $(shell cocotb-config --makefiles)/Makefile.sim

In the terminal, the Axi stream correctly detects the data width to be 32b, but the received frame only has 8b of data:

 0.00ns INFO     cocotb.regression                  Found test test_axis.run_test
     0.00ns INFO     cocotb.regression                  running run_test (1/1)
     0.00ns INFO     cocotb.axis_if.s_axis              AXI stream source
     0.00ns INFO     cocotb.axis_if.s_axis              cocotbext-axi version 0.1.24
     0.00ns INFO     cocotb.axis_if.s_axis              Copyright (c) 2020 Alex Forencich
     0.00ns INFO     cocotb.axis_if.s_axis              https://github.com/alexforencich/cocotbext-axi
     0.00ns INFO     cocotb.axis_if.s_axis              AXI stream source configuration:
     0.00ns INFO     cocotb.axis_if.s_axis                Byte size: 8 bits
     0.00ns INFO     cocotb.axis_if.s_axis                Data width: 32 bits (4 bytes)
     0.00ns INFO     cocotb.axis_if.s_axis              AXI stream source signals:
     0.00ns INFO     cocotb.axis_if.s_axis                tdata width: 32 bits
     0.00ns INFO     cocotb.axis_if.s_axis                tdest: not present
     0.00ns INFO     cocotb.axis_if.s_axis                tid: not present
     0.00ns INFO     cocotb.axis_if.s_axis                tkeep width: 4 bits
     0.00ns INFO     cocotb.axis_if.s_axis                tlast: not present
     0.00ns INFO     cocotb.axis_if.s_axis                tready width: 1 bits
     0.00ns INFO     cocotb.axis_if.s_axis                tuser: not present
     0.00ns INFO     cocotb.axis_if.s_axis                tvalid width: 1 bits
     0.00ns INFO     cocotb.axis_if.s_axis              Reset de-asserted
     0.00ns INFO     cocotb.axis_if.m_axis              AXI stream sink
     0.00ns INFO     cocotb.axis_if.m_axis              cocotbext-axi version 0.1.24
     0.00ns INFO     cocotb.axis_if.m_axis              Copyright (c) 2020 Alex Forencich
     0.00ns INFO     cocotb.axis_if.m_axis              https://github.com/alexforencich/cocotbext-axi
     0.00ns INFO     cocotb.axis_if.m_axis              AXI stream sink configuration:
     0.00ns INFO     cocotb.axis_if.m_axis                Byte size: 8 bits
     0.00ns INFO     cocotb.axis_if.m_axis                Data width: 32 bits (4 bytes)
     0.00ns INFO     cocotb.axis_if.m_axis              AXI stream sink signals:
     0.00ns INFO     cocotb.axis_if.m_axis                tdata width: 32 bits
     0.00ns INFO     cocotb.axis_if.m_axis                tdest: not present
     0.00ns INFO     cocotb.axis_if.m_axis                tid: not present
     0.00ns INFO     cocotb.axis_if.m_axis                tkeep width: 4 bits
     0.00ns INFO     cocotb.axis_if.m_axis                tlast: not present
     0.00ns INFO     cocotb.axis_if.m_axis                tready width: 1 bits
     0.00ns INFO     cocotb.axis_if.m_axis                tuser: not present
     0.00ns INFO     cocotb.axis_if.m_axis                tvalid width: 1 bits
     0.00ns INFO     cocotb.axis_if.m_axis              Reset de-asserted
VCD info: dumpfile axis.vcd opened for output.
VCD warning: $dumpvars: Unsupported argument type (vpiPackage)
     2.00ns INFO     cocotb.axis_if.s_axis              Reset asserted
     2.00ns INFO     cocotb.axis_if.m_axis              Reset asserted
     6.00ns INFO     cocotb.axis_if.s_axis              Reset de-asserted
     6.00ns INFO     cocotb.axis_if.m_axis              Reset de-asserted
Sent frame b'10101010101010101010101010101010'
Sent frame b'11001100110011001100110011001100'
Sent frame b'11110000111100001111000011110000'
    12.00ns INFO     cocotb.axis_if.s_axis              TX frame: AxiStreamFrame(tdata=bytearray(b'10101010101010101010101010101010'), tkeep=None, tid=None, tdest=None, tuser=None, sim_time_start=12000, sim_time_end=None)
    14.00ns INFO     cocotb.axis_if.m_axis              RX frame: AxiStreamFrame(tdata=bytearray(b'1010'), tkeep=[1, 1, 1, 1], tid=[], tdest=[], tuser=[], sim_time_start=14000, sim_time_end=14000)
Received frame [49, 48, 49, 48]
    16.00ns INFO     cocotb.axis_if.m_axis              RX frame: AxiStreamFrame(tdata=bytearray(b'1010'), tkeep=[1, 1, 1, 1], tid=[], tdest=[], tuser=[], sim_time_start=16000, sim_time_end=16000)
Received frame [49, 48, 49, 48]
    18.00ns INFO     cocotb.axis_if.m_axis              RX frame: AxiStreamFrame(tdata=bytearray(b'1010'), tkeep=[1, 1, 1, 1], tid=[], tdest=[], tuser=[], sim_time_start=18000, sim_time_end=18000)
Received frame [49, 48, 49, 48]
    20.00ns INFO     cocotb.axis_if.m_axis              RX frame: AxiStreamFrame(tdata=bytearray(b'1010'), tkeep=[1, 1, 1, 1], tid=[], tdest=[], tuser=[], sim_time_start=20000, sim_time_end=20000)
    22.00ns INFO     cocotb.axis_if.m_axis              RX frame: AxiStreamFrame(tdata=bytearray(b'1010'), tkeep=[1, 1, 1, 1], tid=[], tdest=[], tuser=[], sim_time_start=22000, sim_time_end=22000)
    22.00ns INFO     cocotb.regression                  run_test passed

Did I not read the documentation properly, or is there something that I need to set up differently to be able to work with 32b data?

Any help would be greatly appreciated.

Okay, I had a rough few days, and overlooked the byte data. So I had a nap, and my problem was solved by properly using
await tb.source.send((2251122612).to_bytes(4, byteorder='big')).

Please disregard my issue. The issue was with me.

I guess one thing you should think about is, do you need to work in terms of bytes or in terms of wider words. If you set KEEP_WIDTH = 1, then the AXI stream models should not slice up the cycles into bytes. Similarly, you can omit the tkeep signal and then specify byte_lanes=1 to get the same behavior.

I guess one thing you should think about is, do you need to work in terms of bytes or in terms of wider words. If you set KEEP_WIDTH = 1, then the AXI stream models should not slice up the cycles into bytes. Similarly, you can omit the tkeep signal and then specify byte_lanes=1 to get the same behavior.

Thanks for this idea. I will re-evaluate the bus characteristics, since I will work with 32b words.