寫在前面
達坦科技即將開源的100G RDMA RTL代碼採用cocotb對其功能進行驗證。其中,對於DMA引擎的驗證工作使用了開源的cocotbext-pcie框架來進行驗證。本文將簡要介紹cocotbext-pcie的使用方式,以及在使用中遇到的問題。
對於目前遇到的一些問題,由於需要對cocotbext-pcie本身進行修改,達坦科技計劃在暑期安排一系列夏令營活動,邀請在校生加入對cocotbext-pcie的完善工作,具體活動信息將在近期公佈。
關於cocotb的話題,會有一系列文章進行討論,本篇是此係列的第三篇,歡迎大家關注達坦科技公眾號,第一時間閲讀最新文章。
cocotbext-pcie項目的介紹
cocotbext-pcie是Alex Forencich基於cocotb開發的一套PCIe仿真框架,其原型誕生於Alex Forencic的另一個知名開源網卡項目corundum,由於corundum中大量使用了PCIe接口進行數據傳輸,為了對相關功能進行仿真,Alex基於ccootb編寫了一套仿真環境,這便是cocotbext-pcie的前身。後來,相關的測試框架代碼從corundum的代碼庫中獨立出來,成為了現在的cocotbext-pcie項目。
由於該項目中對PCIe的鏈路層通信過程進行了非常細緻的建模,對於剛接觸PCIe協議的初學者而言,在閲讀PCIe標準spec的同時,如果能夠對照着該項目的Python代碼進行對照學習,對於加深對協議的理解會有非常大的幫助。為了幫助讀者快速熟悉並閲讀項目的代碼,下面來介紹一下該項目的代碼結構,如下所示。
cocotbext
└── pcie
├── core
│ ├── bridge.py
│ ├── caps
│ │ ├── aer.py
│ │ ├── common.py
│ │ ├── __init__.py
│ │ ├── msi.py
│ │ ├── pcie.py
│ │ ├── pm.py
│ ├── device.py
│ ├── dllp.py
│ ├── endpoint.py
│ ├── function.py
│ ├── __init__.py
│ ├── msi.py
│ ├── pci.py
│ ├── port.py
│ ├── rc.py
│ ├── region.py
│ ├── switch.py
│ ├── tlp.py
│ ├── utils.py
│ └── version.py
├── intel
│ ├── ptile
│ │ ├── __init__.py
│ │ ├── interface.py
│ │ └── ptile_model.py
│ └── s10
└── xilinx
└── us
從代碼的組織結構來看,該項目的代碼結構非常清晰易懂,其中core目錄為仿真框架的主體部分,而另外的兩個目錄分別是intel和xilinx兩家公司FPGA PCIe IP核的仿真模型。
仿真框架核心
通過仔細觀察core目錄,可以看到其中包含了用於構建整個PCIe仿真環境的各個組件,通過這些組件,可以非常方便的搭建起一個從RootComplex到FPGA上硬核之間的完整PCIe數據鏈路,可以支持PCIe設備的上電枚舉、Bar空間地址分配、基於Credit機制的流量控制、數據讀寫請求等等功能的仿真。可以看到,該目錄下的文件均與PCIe標準中所定義的內容對應,與具體廠商的硬核實現無關。
下面介紹各幾個比較重要的文件:
- tlp.py:該文件中定義了TLP的數據格式,以及對TLP的各種操作方法。其中定義的Tlp類是用於連接各個廠商具體PCIe硬核模型的重要橋樑。閲讀其代碼有助於理解TLP數據包的組裝與解析。
- region.py:該文件中實現了對一段內存地址的建模,通過該文件所提供的接口,開發者可以模擬PCIe系統中對main memoey的各種操作。
- rc.py:該文件提供了對RootComplex的仿真模型,實現了諸如設備枚舉等功能的仿真。該模塊在實例化的過程中有非常多的初始化參數可以配置,為了瞭解這些參數的作用,閲讀該文件的實現是一個很好的途徑。在編寫測試腳本的過程中,需要實例化一個RootComplex對象,並將一個特定廠商的Device實現連接到RootComplex上。
- dllp.py:該文件描述了DLLP相關的功能,閲讀該文件對理解PCIe協議中基於credit的流控機制有很好的幫助。
- function.py:該文件中定義的Function類型是眾多其他類型的基類,實現了對於PCIe設備配置空間的行為建模,閲讀該文件有助於理解PCIe設備配置空間的相關內容。
廠商器件支持
再來看另外兩個目錄,以intel/ptile目錄為例,這是對於Intel的PTile硬核所建立的仿真模型。其中interface.py裏面定義了與user logic之間的接口,而ptile_model.py裏面則定義了PTile硬核的行為,該文件的重要功能之一就是將上文中tlp.py中所定義的抽象TLP數據結構轉換為廠商私有的格式,再通過interface.py與用户編寫的DUT模塊相連接。
可以看到,目前項目中支持的PCIe硬核只有Intel和Xilinx兩家公司的產品,而且型號僅限於4條產品線。達坦科技的400G RDMA網卡方案使用了Intel的RTile硬核來提供PCIe Gen5接口,而目前cocotbext-pcie內置的模型僅支持到PTile,為此,達坦科技嘗試為其編寫了RTile的器件適配代碼;受限於篇幅,將在另一篇文章中展開介紹如何實現對新器件的適配。對於感興趣的同學,可以先閲讀仍處於開發測試階段的開源代碼,待驗證穩定後會向上遊發起合併:
https://github.com/myrfy001/cocotbext-pcie
cocotbext-pcie的簡單使用方式
通常,基於該框架開發的測試腳本,需要包括以下準備工作:
- 實例化一個RootComplex對象,作為主機側的控制入口;實例化一個特定廠商的PCIe硬核模型對象,提供與DUT對接的接口。
- 配置內存地址相關的參數,對於RootComplex對象而言,需要為其主備一塊用於模擬main memory的內存區域,對於廠商PCIe硬核而言,需要為其配置好BAR地址區間。
- 將DUT的接口與PCIe硬核模型接口進行對接。
- 將PCIe硬核對象與RootComplex對象連接,並啓動enumerate過程。
- 通過BDF查找到需要操作的device,並對其進行下一步操作
經過上述步驟後,就已經準備好了測試前的基礎工作了。接下來可以進行兩方面的測試工作: - 對BAR內存地址範圍內的數據進行讀寫,模擬CPU側向device側發起request,檢查device的response。
- 從device直接發起DMA請求,對main memory進行讀寫操作,然後檢查main memory的值是否符合預期。
基於上述流程,給出如下一段示例代碼,展示基本的使用步驟:
from cocotbext.pcie.core import RootComplex
from cocotbext.pcie.xilinx.us import UltraScalePlusPcieDevice
from cocotbext.axi import AxiStreamBus
# 實例化RootComplex實例
rc = RootComplex()
rc.max_payload_size = 1
rc.max_read_request_size = 2
# 實例化某廠商的PCIe硬核模型
dev_model = UltraScalePlusPcieDevice(
# 將dut與PCIe硬核模型通過AXI總線進行連接
rq_bus=AxiStreamBus.from_prefix(dut, "m_axis_rq"),
rc_bus=AxiStreamBus.from_prefix(dut, "s_axis_rc"),
cq_bus=AxiStreamBus.from_prefix(dut, "s_axis_cq"),
cc_bus=AxiStreamBus.from_prefix(dut, "m_axis_cc"),
# 省略若干參數...
)
# 在RootComplex側模擬出一塊大小為2MB的Main Memory
main_mem = rc.mem_pool.alloc_region(2*1024*1024)
# 為硬核模型的function 0添加兩個BAR地址空間
dev_model.functions[0].configure_bar(0, 16*1024*1024)
dev_model.functions[0].configure_bar(1, 16*1024)
# 連接設備和RootComplex(模擬把卡插到PCIe插槽上)
root_port = rc.make_port()
root_port.connect(dev_model)
# 執行PCIe的枚舉過程,由於枚舉過程涉及到實際的TLP傳輸,會推進仿真時間,需要await
await tb.rc.enumerate()
# 通過BDF找到要操作的具體的device,注意這裏的device和上面的PCIe硬核模型是不一樣的
dev = rc.find_device(dev_model.functions[0].pcie_id)
# 對設備進行配置,需要await
await dev.enable_device()
await dev.set_master()
# 環境初始化完成,下面進行實際的功能驗證。
# 下列代碼演示了一個通過寫入CSR啓動DMA傳輸的例子,展示瞭如何仿真CPU發起BAR空間訪問和設備發起DMA訪問。
# 初始化main memory
main_mem[0] = 0xab
main_mem[1] = 0x00
# 獲取設備BAR的handle,模擬host側對device BAR的訪問
dev_bar1 = dev.bar_window[1]
# 向BAR空間地址0x100的位置寫入Dword數字1,觸發硬件DMA傳輸
await dev_bar1.write(0x100, (1).to_bytes(4, byteorder='little', signed=False))
# 等待DMA傳輸完成
await Timer(5000, "ns")
# 檢查DMA引擎是否工作正常
assert main_mem[1] == 0xab
目前cocotbext-pcie所存在的一些缺陷
雖然該開源項目為PCIe相關的應用提供了一個簡單易用的仿真驗證手段,但由於該開源項目脱胎於開源網卡項目,其仿真模型主要覆蓋了實現一個普通網卡設備所需要的PCIe功能,故而對PCIe規範中的功能支持並不是完整的,使用過程中仍有一些坑需要避免。以下是達坦科技在使用過程中遇到的一些問題:
- 不支持原子操作類型,無法用於驗證原子操作的正確性。
- 不支持TLP中AT字段非零的情況。
- 無法模擬RootComplex側的訪存延遲。
- 支持的PCIe硬核模型種類較少。
達坦科技的開源夏令營計劃
針對上述存在的問題,達坦科技希望召集對cocotb驗證框架感興趣的同學,利用暑假實踐,參與到達坦科技的開源夏令營活動中,為cocotbext-pcie完善功能,回饋開源社區,同時獲取達坦的獎金和實習機會。活動詳情會在近期公佈,歡迎大家關注達坦科技的微信公眾號以獲取最新消息。
達坦科技始終致力於打造高性能AI+Cloud基礎設施平台,積極推動AI應用的落地。達坦科技通過軟硬件深度融合的方式,提供AI推理引擎和高性能網絡,為AI應用提供彈性、便利、經濟的基礎設施服務,以此滿足不同行業客户對AI+Cloud的需求。
公眾號:達坦科技DatenLord
DatenLord官網:
https://datenlord.github.io/zh-cn/
知乎賬號:
https://www.zhihu.com/org/da-tan-ke-ji
B站:
https://space.bilibili.com/2017027518
郵箱:info@datenlord.com
如果您有興趣加入達坦科技Rust前沿技術交流羣、硬件敏捷開發和驗證方法學討論羣或blue-rdma交流羣,請添加小助手微信:DatenLord_Tech