【Cocotb】cocotbext-axi拓展库

用于Cocotb的AXI接口模块,GitHub仓库: https://github.com/alexforencich/cocotbext-axi

介绍

AXI、AXI lite和AXIStream仿真模型

安装

从pip安装(发布版本,稳定):

$ pip install cocotbext-axi

从git安装(最新开发版本,可能不稳定):

$ pip install https://github.com/alexforencich/cocotbext-axi/archive/master.zip

主动开发的安装:

$ git clone https://github.com/alexforencich/cocotbext-axi
$ pip install -e cocotbext-axi

文档和使用示例

请参阅tests目录、verilog-axiverilog-axis以获取使用这些模块的完整测试台。

AXI和AXI lite Master

AxiMaster和AxiLiteMaster类实现AXI主机功能,并能够对AXI从机进行读写操作。 请求操作将根据AXI规范进行拆分和对齐。 AxiMaster模块能够生成窄突发,处理多个进行中的操作,以及处理跨不同事务ID的响应中的重新排序和交织。 AxiMaster和AxiLiteMaster以及相关的对象都扩展了Region,因此它们可以附加到AddressSpace对象上以处理指定区域中的内存操作。

AxiMaster是围绕AxiMasterWrite和AxiMasterRead的包装器。 类似地AxiLiteMaster是围绕AxiLiteMasterWrite和AxiLiteMasterRead的包装器。 如果需要只读或只写接口而非全接口,可使用相应的只读或只写变体,用法和API完全相同。

要使用这些模块,请导入您需要的模块并将其连接到DUT:

from cocotbext.axi import AxiBus, AxiMaster

axi_master = AxiMaster(AxiBus.from_prefix(dut, "s_axi"), dut.clk, dut.rst)

构造函数的第一个参数接受AxiBus或AxiLiteBus对象,视情况而定。 这些对象是接口信号的容器,并包含自动连接的类方法。一旦模块被实例化就可以通过几种不同的方式启动读和写操作。

首先,可以使用异步阻塞的read()、write()及其相关的字访问包装器进行操作。从不同协程启动的多个并发操作会被正确处理,并按照操作完成的顺序返回结果。例如:

await axi_master.write(0x0000, b'test')
data = await axi_master.read(0x0000, 4)

可以指定其他参数来控制边带信号和突发设置。传输将根据AXI规范拆分为一个或多个突发。从对read()或write()的相同调用中生成的所有突发将使用相同的ID,如果未指定,该ID将自动生成。Read()和write()返回包含地址、数据或长度以及resp的namedtuple对象。这是首选样式,也是字节访问包装器支持的唯一样式。

或可以使用非阻塞的init_read()和init_write()初始化操作。这些函数返回操作完成时触发的Event对象,并且可以从Event.data中检索结果。例如:

write_op = axi_master.init_write(0x0000, b'test')
await write_op.wait()
resp = write_op.data
read_op = axi_master.init_read(0x0000, 4)
await read_op.wait()
resp = read_op.data

使用此方法,可以从同一个协程启动多个并发操作。还可以将事件与Combine、First和with_timeout一起使用。

AxiMaster 和 AxiLiteMaster 构造函数参数

  • bus:包含AXI接口信号的AxiBus或AxiLiteBus对象
  • clock:时钟信号
  • reset:复位信号(可选)
  • reset_active_level:重置活动级别(可选,默认True)

AxiMaster附加参数

  • max_burst_len:以周期为单位的最大脉冲串长度,范围1-256,默认值256。

方法

  • init_read(address, length, ...):从address开始读取length字节数据。 返回Event对象。
  • init_write(address, data, ...):从address开始开始写入data(字节)。 返回Event对象。
  • idle():当没有未完成的操作正在进行时,返回True
  • wait():阻塞等待,直到所有未完成的操作完成
  • wait_read():等待所有未完成的读取操作完成
  • wait_write():等待所有未完成的写入操作完成
  • read(address, length, ...):从address开始读取length字节
  • read_words(address, count, byteorder='little', ws=2, ...):从address开始读取count个ws字节
  • read_dwords(address, count, byteorder='little', ...):从address开始读取count个4字节
  • read_qwords(address, count, byteorder='little', ...):从address开始读取count个8字节
  • read_byte(address, ...):读取address处的单个字节
  • read_word(address, byteorder='little', ws=2, ...):读取address处的单个ws字节字
  • read_dword(address, byteorder='little', ...):读取address处单个4字节字
  • read_qword(address, byteorder='little', ...):读取address处的单个8字节字
  • write(address, data, ...):从address开始写入data(字节)
  • write_words(address, data, byteorder='little', ws=2, ...):从address开始写入data(ws字节字)
  • write_dwords(address, data, byteorder='little', ...):从address开始写入data(4字节双字)
  • write_qwords(address, data, byteorder='little', ...):从address开始写入data(8字节qwords)
  • write_byte(address, data, ...):在address处写入data单个字节data
  • write_word(address, data, byteorder='little', ws=2, ...):在address处写入data单个ws字节字
  • write_dword(address, data, byteorder='little', ...):在address处写入data单个4字节双字
  • write_qword(address, data, byteorder='little', ...):在address处写入data单个8字节qword

AxiMaster的附加可选项

  • arid,awid:突发的AXI ID,默认自动分配
  • burst:AXI突发类型,default AxiBurstType.INCR
  • size:AXI突发大小,接口支持的默认最大值
  • lock:AXI锁类型,默认AxiLockType.NORMAL
  • cache:AXI缓存字段,默认0b0011
  • prot:AXI保护标志,默认AxiProt.NONSECURE
  • qos:AXI QOS字段,默认0
  • region:AXI region字段,默认0
  • user:AXI用户信号(awuser/aruser),默认0
  • wuser:AXI wuser信号,默认值0(仅限写相关方法)
  • event:Event对象用于等待并获取特定操作的结果,默认为None。 当操作完成并通过Event.data返回结果时,将触发该事件。 (仅限init_read()和init_write())

AxiLiteMaster的附加可选项

  • prot:AXI保护标志,默认AxiProt.NONSECURE
  • event:Event对象用于等待并获取特定操作的结果,默认为None。 当操作完成并通过Event.data返回结果时,将触发该事件。 (仅限init_read()和init_write())

AxiBus和AxiLiteBus

AxiBus、AxiLiteBus和相关对象是接口信号的容器,包含了各个通道的总线对象实例,扩展自cocotb_bus.bus.Bus类。提供了from_entity和from_prefix类方法来方便信号名称匹配。对于AXI接口,请使用适当的AxiBus、AxiReadBus或AxiWriteBus。对于AXI lite接口,请使用适当的AxiLiteBus、AxiLiteReadBus或AxiLiteWriteBus。

AXI和AXI lite slave

AxiSlave和AxiLiteSlave类实现了AXI从机,能够完成来自上游AXI主机的读写操作。AxiSlave模块能够处理窄突发。这些模块既可以用于代表DUT在MemoryInterface上执行内存读取和写入,也可以扩展它们以实现自定义功能。

AxiSlave是AxiSlaveWrite和AxiSlaveRead的包装器。类似地,AxiLiteSlave是AxiLiteSlaveWrite和AxiLiteSlaveRead的包装。如果需要只读或只写接口而非全接口,请使用相应的只读或只写变体,其用法和API完全相同。

要使用这些模块,请导入您需要的模块并将其连接到DUT:

from cocotbext.axi import AxiBus, AxiSlave, MemoryRegion

axi_slave = AxiSlave(AxiBus.from_prefix(dut, "m_axi"), dut.clk, dut.rst)
region = MemoryRegion(2**axi_slave.read_if.address_width)
axi_slave.target = region

构造函数的第一个参数接受一个AxiBus或AxiLiteBus对象。这些对象是接口信号的容器,并包含自动化连接的类方法。

也可以扩展这些模块;可以通过重写内部的_read()和_write()方法来定制操作。参见AxiRam和AxiLiteRam的例子。

AxiSlave和AxiLiteSlave构造函数参数

bus:包含AXI接口信号的AxiBus或AxiLiteBus对象
clock:时钟信号
reset:复位信号(可选)
reset_active_level:重置活动级别(可选,默认True)
target:目的域(可选,默认None)
属性
target:目的域

AXI和AXI lite RAM

AxiRam和AxiLiteRam类实现了AXI RAM,并能够完成来自上游AXI主机的读写操作。AxiRam模块能够处理窄突发传输。这些模块是相应的AxiSlave和AxiLiteSlave模块的扩展。在内部使用SparseMemory来支持仿真非常大的存储器。

AxiRam是AxiRamWrite和AxiRamRead的包装器,类似地AxiLiteRam是AxiLiteRamWrite和AxiLiteRamRead的包装。如果需要只读或只写接口而非全接口,请使用相应的只读或只写变体,其用法和API完全相同。

要使用这些模块,请导入您需要的模块并将其连接到DUT:

from cocotbext.axi import AxiBus, AxiRam

axi_ram = AxiRam(AxiBus.from_prefix(dut, "m_axi"), dut.clk, dut.rst, size=2**32)

构造函数的第一个参数接受一个AxiBus或AxiLiteBus对象。这些对象是接口信号的容器,并包含自动化连接的类方法。

模块实例化后,可以通过几种不同的方式访问内存内容。首先,可以通过mem属性直接访问mmap对象。其次,可以使用read()、write()和各种字节访问包装器。还提供了十六进制转储帮助器方法用于调试。例如:

axi_ram.write(0x0000, b'test')
data = axi_ram.read(0x0000, 4)
axi_ram.hexdump(0x0000, 4, prefix="RAM")

多端口存储器可以通过将第一个实例的mem对象传递给其他实例来构造, 例如以下是如何创建一个四端口RAM:

axi_ram_p1 = AxiRam(AxiBus.from_prefix(dut, "m00_axi"), dut.clk, dut.rst, size=2**32)
axi_ram_p2 = AxiRam(AxiBus.from_prefix(dut, "m01_axi"), dut.clk, dut.rst, mem=axi_ram_p1.mem)
axi_ram_p3 = AxiRam(AxiBus.from_prefix(dut, "m02_axi"), dut.clk, dut.rst, mem=axi_ram_p1.mem)
axi_ram_p4 = AxiRam(AxiBus.from_prefix(dut, "m03_axi"), dut.clk, dut.rst, mem=axi_ram_p1.mem)

AxiRam和AxiLiteRam构造函数参数

bus:包含AXI接口信号的AxiBus或AxiLiteBus对象
clock:时钟信号
reset:复位信号(可选)
reset_active_level:重置活动级别(可选,默认True)
size:内存大小(以字节为单位)(可选,默认值2**64)
mem:要使用的mmap或SparseMemory支持对象(可选,覆盖大小)

属性

mem:直接访问共享的mmap或SparseMemory备份对象

方法

  • read(address,length):从address开始读取length字节
  • read_byte(address):读取address上的单个字节
  • read_dword(address,byteorder='little'):读取address上的单个4字节dword
  • read_dwords(address,count, byteorder='little'):从address开始读取count个4字节
  • read_qword(address,byteorder='little'):读取address处的单个8字节qword
  • read_qwords(address,count, byteorder='little'):从address开始读取count个8字节
  • read_word(address,byteorder='little', ws=2):在address处读取单个ws-byte字
  • read_words(address,count, byteorder='little', ws=2):读取从address开始读取count个ws-byte字
  • write(address,data):从address开始写数据(字节)
  • write_byte(address,data):在address上写单个字节
  • write_word(address,data, byteorder='little', ws=2):在address上写单个ws字节
  • write_words(address,data,byteorder='little', ws=2):写入数据(ws-byte字),从address开始
  • write_dword(address,data,byteorder='little'):在address上写入单个4字节的dword
  • write_dwords(address,data,byteorder='little'):从address开始写数据(4字节的dwords)
  • write_qword(address,data,byteorder='little'):在address上写一个8字节的qword
  • write_qwords(address,data,byteorder='little'):从address开始写数据(8字节的qwords)
  • hexdump(address,length,prefix= ' '):从address开始返回length字节的十六进制转储,前缀行可选前缀
  • hexdump_line(address,length,prefix= ' '):从address开始返回length字节的十六进制转储(str列表),前缀行可选前缀
  • hexdump_str(address,length,prefix= ' '):从address开始返回length字节的十六进制转储(str),前缀行可选

AXI stream

AxiStreamSource、AxiStreamSink和AxiStreamMonitor类可用于驱动、接收和监控AXI流接口上的流量。 AxiStreamSource驱动除tready以外的所有信号,可用于将AXI流流量驱动到设计中。 AxiStreamSink仅驱动tready线路,因此可以接收AXI流业务并施加背压。 AxiStreamMonitor不驱动任何信号,因此可以连接到设计中的任何地方的AXI流接口,以被动监控流量。

要使用这些模块,请导入您需要的模块并将其连接到DUT:

from cocotbext.axi import (AxiStreamBus, AxiStreamSource, AxiStreamSink, AxiStreamMonitor)

axis_source = AxiStreamSource(AxiStreamBus.from_prefix(dut, "s_axis"), dut.clk, dut.rst)
axis_sink = AxiStreamSink(AxiStreamBus.from_prefix(dut, "m_axis"), dut.clk, dut.rst)
axis_mon= AxiStreamMonitor(AxiStreamBus.from_prefix(dut.inst, "int_axis"), dut.clk, dut.rst)

构造函数的第一个参数接受一个AxiStreamBus对象。 这个对象是接口信号的容器,包括自动连接的类方法。

使用AxiStreamSource将数据发送到设计中,调用send()/send_nowait()或write()/write_nowait()。可接受的数据类型是可迭代对象或AxiStreamFrame对象。可选地,调用wait()来等待传输操作完成。例子:

await axis_source.send(b'test data')
# wait for operation to complete (optional)
await axis_source.wait()

也可以通过在AxiStreamFrame对象的tx_complete字段中传递事件,然后等待该事件来等待特定帧的传输完成。 设置了模拟时间字段的帧将在事件数据中返回。 例子:

frame = AxiStreamFrame(b'test data', tx_complete=Event())
await axis_source.send(frame)
await frame.tx_complete.wait()
print(frame.tx_complete.data.sim_time_start)

要使用AxiStreamSink或AxiStreamMonitor接收数据,请调用recv()/recv_nowait()或read()/read_nowait()。 可选地调用wait()以等待新的接收数据。 recv()用于面向帧的接口,默认情况下在返回之前压缩AxiStreamFrames。 read()用于非面向帧的流。 调用read()在内部调用队列中当前所有帧的recv(),然后压缩所有帧中的tdata并将其合并到一个单独的读取队列中,从该队列返回读取数据。 所有边带数据会被丢弃。

data = await axis_sink.recv()

信号

  • tdata:数据,必填
  • tvalid:限定所有其他信号;可选,默认为1
  • tready:表示接收器已准备好接收数据;可选,默认为1
  • tlast:标记帧的最后一个周期;可选,默认为1
  • tkeep:限定数据字节,数据总线宽度必须能被tkeep信号宽度整除;可选,默认为1
  • tid:ID信号,可用于路由;可选,默认为0
  • tdest:目的地信号,可用于路由;可选,默认为0
  • tuser:附加用户数据;可选,默认为0

构造函数参数

  • bus:包含AXI流接口信号的AxiStreamBus对象
  • clock:时钟信号
  • reset:复位信号(可选)
  • reset_active_level:重置活动级别(可选,默认True)
  • byte_size:字节大小(可选)
  • byte_lanes:字节通道计数(可选)

注意:byte_size、byte_lanes、len(tdata)和len(tkeep)都是相关的,因为byte_lanes是从tkeep(如果连接)和byte_size*byte_lanes == len(tdata)设置的。 因此,如果连接了tkeep,byte_size和byte_lanes都将在内部计算,并且不能被覆盖。 如果tkeep未连接,则可以指定byte_size或byte_lanes,另一个将被计算,使得byte_size*byte_lanes == len(tdata)。

属性

  • pause:暂停接口(deassert tready或tvalid)(source/sink only)
  • queue_occupancy_bytes:队列中的字节数(all)
  • queue_occupancy_frames:队列中的帧数(all)
  • queue_occupancy_limit_bytes:在应用反压之前允许的队列中的最大字节数(source/sink only)
  • queue_occupancy_limit_frames:在应用反压之前允许的队列中的最大帧数(source/sink only)

方法

  • send(frame):发送帧(阻塞)(source)
  • send_nowait(frame):发送帧(非阻塞)(source)
  • write(data):发送数据(等同于send)(阻塞)(source)
  • write_nowait(data):发送数据(等同于send_nowait)(非阻塞)(源代码)
  • recv(compact=True):接收帧作为GmiiFrame(阻塞)(sink)
  • recv_nowait(compact=True):接收帧作为GmiiFrame(非阻塞)(sink)
  • read(count):从缓冲区读取计数字节(阻塞)(sink/monitor)
  • read_nowait(count):从缓冲区读取计数字节(非阻塞)(sink/monitor)
  • count():返回队列中的项目数(all)
  • empty():如果队列为空,则返回True(all)
  • full():如果满足队列占用限制(source/sink),则返回True
  • idle():如果没有传输正在进行(all)或队列不为空(source),则返回True
  • clear():删除队列中的所有数据(all)
  • wait():等待空闲(source)
  • wait(timeout=0, timeout_unit='ns'):等待接收帧(sink)
  • set_pause_generator(generator):为暂停信号设置发生器,发生器将在每个时钟周期(source/sink)提前
  • clear_pause_generator():删除暂停信号发生器(source/sink)
AxiStreamBus对象

AxiStreamBus对象是接口信号的容器,目前是cocotb.bus.Bus的扩展。 提供类方法from_entity和from_prefix以促进信号名称匹配。

AxiStreamFrame对象

AxiStreamFrame对象是要经由AXIStream传输的帧的容器。 tdata字段包含字节列表形式的分组数据,如果字节大小是8位,则该字段是bytearray,否则是list中的int。 tkeep、tid、tdest和tuser可以是None中的int、list或int。

属性

  • tdata:bytes、bytearray或list
  • tkeep:tkeep字段,可选;列表,每个条目限定tdata中的相应条目。 可用于在源端插入间隙。
  • tid:tid字段,可选; int或列表,每个tdata一个条目,发送时每个周期使用的最后一个值。
  • tdest:tdest字段,可选; int或列表,每个tdata一个条目,发送时每个周期使用的最后一个值。
  • tuser:tuser字段,可选; int或列表,每个tdata一个条目,发送时每个周期使用的最后一个值。
  • sim_time_start:帧的第一个传输周期的模拟时间。
  • sim_time_end:帧最后一个传输周期的模拟时间。
  • tx_complete:帧传输时触发的事件或可调用。

方法

  • normalize(): 将tkeep, tid, tdest和tuser打包到与tdata相同的长度,必要时复制最后一个元素,初始化tkeep为list为1,如果没有指定,则初始化tid, tdest和tuser为list为0。
  • compact():删除基于tkeep的tdata, tid, tdest和tuser值,如果所有值都相同,则删除tkeep,将tid, tdest和tuser压缩为int。

地址空间抽象

地址空间抽象提供了一个框架,用于交叉连接多个内存映射接口,用于测试与复杂系统(包括具有DMA引擎的组件)接口的组件。

MemoryInterface是地址空间抽象中所有组件的基类。MemoryInterface提供了核心的read()和write()方法,实现了边界检查以及字节访问包装器,提供了创建Window和WindowPool对象的方法。函数get_absolute_address()将地址转换为系统地址空间。MemoryInterface可以通过覆盖_read()和_write()来扩展以实现自定义功能。

Window对象表示具有一定长度和偏移量的父地址空间上的视图。 Window上的read()write()操作被转换为父地址空间上的等效操作。 多个Window实例可以重叠并访问地址空间的同一部分。

WindowPool提供了一种从地址空间的一部分动态分配窗口的方法。 它使用标准的内存管理算法来提供所请求大小的自然对齐的Window对象。

Region是实现地址空间一部分的所有组件的基类。 Region对象可以与AddressSpace对象注册,以处理指定区域中的read()和write()操作。 Region可以通过实现一部分地址空间的组件进行扩展。

MemoryRegionRegion的扩展,使用mmap实例来处理内存操作。 MemoryRegion还提供了十六进制转储方法以及索引和切片。

SparseMemoryRegion类似于MemoryRegion,但由SparseMemory而不是mmap支持,因此可以模拟非常大的地址空间区域。

PeripheralRegionRegion的扩展,可以包装另一个实现read()write()的对象,作为扩展Region的替代方案。

AddressSpace是处理地址空间的核心对象。 Region对象可以用指定的基址、大小和偏移量注册到AddressSpace。 然后,AddressSpace对象将read()write()操作定向到适当的Regions,在必要时适当地拆分请求并转换地址。 用offset而不是None登记的区域被转换,使得对基地址+ N的访问映射到N +偏移。 注册为offsetNone的区域不会被翻译。 与同一Region注册的AddressSpace对象不能重叠,但同一Region可以注册多次。 AddressSpace还提供了创建Pool对象的方法。

PoolAddressSpace的扩展,支持MemoryRegions的动态分配。 它使用标准的内存管理算法来提供所请求大小的自然对齐的MemoryRegion对象。

例子

这是一个简单的示例,展示了如何使用地址空间抽象组件将DUT连接到模拟主机系统,包括模拟RAM、来自DUT的用于DMA的AXI接口以及到DUT的用于控制的AXI lite接口。

from cocotbext.axi import AddressSpace, SparseMemoryRegion
from cocotbext.axi import AxiBus, AxiLiteMaster, AxiSlave

# 系统地址空间
address_space = AddressSpace(2**32)

# RAM
ram = SparseMemoryRegion(2**24)
address_space.register_region(ram, 0x0000_0000)
ram_pool = address_space.create_window_pool(0x0000_0000, 2**20)

# DUT控制寄存器接口
axil_master = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_axil_ctrl"), dut.clk, dut.rst)
address_space.register_region(axil_master, 0x8000_0000)
ctrl_regs = address_space.create_window(0x8000_0000, axil_master.size)

# DMA from DUT
axi_slave = AxiSlave(AxiBus.from_prefix(dut, "m_axi_dma"), dut.clk, dut.rst, target=address_space)

# 测试DUT DMA功能
src_block = ram_pool.alloc_window(1024)
dst_block = ram_pool.alloc_window(1024)

test_data = b'test data'
await src_block.write(0, test_data)

await ctrl_regs.write_dword(DMA_SRC_ADDR, src_block.get_absolute_address(0))
await ctrl_regs.write_dword(DMA_DST_ADDR, dst_block.get_absolute_address(0))
await ctrl_regs.write_dword(DMA_LEN, len(test_data))
await ctrl_regs.write_dword(DMA_CONTROL, 1)

while await ctrl_regs.read_dword(DMA_STATUS) == 0:
    pass

assert await dst_block.read(0, len(test_data)) == test_data

AXI信号

  • 写地址通道
    • awid:交易ID
    • awaddr:地址
    • awlen:突发长度(周期)
    • awsize:突发大小(字节/周期)
    • awburst:突发类型
    • awlock:锁类型
    • awcache:缓存控制
    • awprot:保护位
    • awqos:QoS字段
    • awregion:区域字段
    • buser:附加用户边带数据
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 写数据通道
    • wdata:写入数据
    • wstrb:写选通
    • wlast:脉冲串结束标志
    • buser:附加用户边带数据
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 写响应信道
    • awid:交易ID
    • bresp:写响应
    • buser:附加用户边带数据
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 读地址通道
    • awid:交易ID
    • awaddr:地址
    • awlen:突发长度(周期)
    • awsize:突发大小(字节/周期)
    • awburst:突发类型
    • awlock:锁类型
    • awcache:缓存控制
    • awprot:保护位
    • awqos:QoS字段
    • awregion:区域字段
    • buser:附加用户边带数据
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 读数据通道
    • awid:交易ID
    • rdata:读取数据
    • rresp:读取响应
    • wlast:脉冲串结束标志
    • buser:附加用户边带数据
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压

AXI lite信号

  • 写地址通道
    • awaddr:地址
    • awprot:保护位
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 写数据通道
    • wdata:写入数据
    • wstrb:写选通
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 写响应信道
    • bresp:写响应
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 读地址通道
    • awaddr:地址
    • awprot:保护位
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • 读数据通道
    • rdata:读取数据
    • rresp:读取响应
    • awvalid:有效信号,限定所有通道字段
    • bready:就绪信号,来自后级模块的背压
  • AXI流信号

  • tdata:数据
  • tvalid:限定所有其他信号
  • tready:表示接收器已准备好接收数据
  • tlast:标记帧的最后一个周期
  • tkeep:限定tdata中的数据字节
  • tid:ID信号,可用于路由
  • tdest:目的地信号,可用于路由
  • tuser:附加边带数据
  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

惜缘若水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值