Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

AMD XDNA Driver for Linux を覗いてみる (その1)

はじめに

このブログでも何度か取り上げた、AMD XDNA (Xilinx AI Engine) 。その Linux の driver が github に公開されました。

github.com

今回は、github の中をさっくり、覗いてみます。

どんな構成

Introduction に下記のような記載があります。

This repository is for supporting XRT on AMD XDNA devices. From this repository, you can build a XRT plugin DEB package. On a machine with XDNA device, with both XRT and XRT plugin packages installed, user can start using XDNA device on Linux.

そう、XRT、って、Xilinx の XRT です。つまり、Xilinx Runtime を使うわけです。

amdxdna

ドライバの本体は、amdxdna です。

amdxdna_drv.c の中を覗いてみたら、どうやら、PCIバイスとなっています。

static const struct pci_device_id pci_ids[] = {
    { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1502),
        .class = PCI_CLASS_SP_OTHER << 8,  /* Signal Processing */
        .class_mask = 0xFFFF00,
        .driver_data = DEV_INFO_TO_DATA(1502),
    },
    { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x17f0),
        .class = PCI_CLASS_SP_OTHER << 8,  /* Signal Processing */
        .class_mask = 0xFFFF00,
        .driver_data = DEV_INFO_TO_DATA(17f0),
    },
    {0}
};
MODULE_DEVICE_TABLE(pci, pci_ids);

バイスIDが 0x1502 って、

で出てきた、IPU_device ですね。

で、0x17f0 は?

下記のように、PCI Driver になっていますね。

static struct pci_driver amdxdna_pci_driver = {
    .name = KBUILD_MODNAME,
    .id_table = pci_ids,
    .probe = amdxdna_probe,
    .remove = amdxdna_remove,
    .driver.pm = &amdxdna_pm_ops,
};

module_pci_driver(amdxdna_pci_driver);

IPU の初期化

XDNA は、IPUなので、IPUの初期化の部分(ipu_init)をみてみます。

 ret = amdxdna_iommu_mode_setup(xdna);

    ret = iommu_dev_enable_feature(&xdna->pdev->dev, IOMMU_DEV_FEAT_SVA);

で、IOMMUを使っています。

 ret = ipu_setup_pcidev(idev);

が IPU の初期化の部分ですかね。後で中をみてみます。

 tbl = pcim_iomap_table(pdev);

    idev->sram_base = tbl[xdna->dev_info->sram_bar];
    idev->smu_base = tbl[xdna->dev_info->smu_bar];

SRAM と SMU の BAR の設定

 ret = ipu_smu_init(idev);

SMU の初期化。。後で中をみてみます。

 idev->psp_hdl = amdxdna_psp_create(&pdev->dev, &psp_conf);

PSP って何だろうか?あとでみてみます

 ret = ipu_get_mgmt_chann_info(idev);

FIrmware 関連っぽいです。

 mbox_res.ringbuf_base = (u64)idev->sram_base;
    mbox_res.ringbuf_size = pci_resource_len(xdna->pdev, xdna->dev_info->sram_bar);
    mbox_res.mbox_base = (u64)tbl[xdna->dev_info->mbox_bar];
    mbox_res.mbox_size = MBOX_SIZE(idev);
    mbox_res.name = "xdna_mailbox";
    xdna->mbox = xdna_mailbox_create(&mbox_res);

SRAMのBARを使って、Message Boxを使っています。

 mgmt_mb_irq = pci_irq_vector(xdna->pdev, idev->mgmt_chan_idx);

割り込み

 xdna_mailbox_intr_reg = idev->mgmt_i2x.mb_head_ptr_reg + 4;
    xdna->mgmt_chann = xdna_mailbox_create_channel(xdna->mbox,
                               &idev->mgmt_x2i,
                               &idev->mgmt_i2x,
                               xdna_mailbox_intr_reg,
                               mgmt_mb_irq);

Message Box の生成

 ret = ipu_mgmt_fw_init(idev);

Firmware の初期化

 xrs_cfg.clk_list.num_levels = 3;
    xrs_cfg.clk_list.cu_clk_list[0] = 0;
    xrs_cfg.clk_list.cu_clk_list[1] = 800;
    xrs_cfg.clk_list.cu_clk_list[2] = 1000;
    xrs_cfg.sys_eff_factor = 1;
    xrs_cfg.actions = &ipu_xrs_actions;
    xrs_cfg.total_col = idev->metadata.cols;
    xrs_cfg.mode = XRS_MODE_TEMPORAL_BEST;
    idev->xrs_hdl = xrs_init(&xrs_cfg);

xrs って、何だろうか?後でみてみます。

 xdna->async_msgd = kthread_run(ipu_error_async_msg_thread, xdna, "async_msgd");

Firmwareからの非同期エラーを処理するkthreadの立ち上げ

iipu_setup_pcidev

ipu_setup_pcidev

 ret = dma_set_mask_and_coherent(&xdna->pdev->dev, DMA_BIT_MASK(64));

64ビットのDMA

 ret = pcim_enable_device(xdna->pdev);

    pci_set_master(xdna->pdev);

PCI Device & Master Device

 bar_mask = pci_select_bars(xdna->pdev, IORESOURCE_MEM);
    ret = pcim_iomap_regions(xdna->pdev, bar_mask, "amdxdna-ipu");

BARの設定

 nvec = pci_msix_vec_count(xdna->pdev);
    ret = pci_alloc_irq_vectors(xdna->pdev, nvec, nvec, PCI_IRQ_MSIX);

MSIX の設定

ipu_smu_init

 ret = ipu_smu_set_power_on(idev);

    ret = ipu_smu_set_mpipu_clock_freq(idev, SMU_MPIPUCLK_FREQ_MAX);

    ret = ipu_smu_set_hclock_freq(idev, SMU_HCLK_FREQ_MAX);

どうやら、電力とクロックの設定のようです。

amdxdna_psp_create

 ret = psp_load_firmware(psp);

PSPfirmware をダウンロード

 reg_vals[0] = PSP_VALIDATE;
    reg_vals[1] = lower_32_bits(psp->fw_paddr);
    reg_vals[2] = upper_32_bits(psp->fw_paddr);
    reg_vals[3] = psp->fw_buf_sz;

    ret = psp_exec(psp, reg_vals);

    memset(reg_vals, 0, sizeof(reg_vals));
    reg_vals[0] = PSP_START;
    reg_vals[1] = PSP_START_COPY_FW;
    ret = psp_exec(psp, reg_vals);

PSPを起動!

xrs_init

void *xrs_init(struct init_config *cfg)
{
    struct solver_rgroup *rgp;
    struct solver_state *xrs;

    xrs = kzalloc(sizeof(*xrs), GFP_KERNEL);
    if (!xrs)
        return NULL;

    memcpy(&xrs->cfg, cfg, sizeof(struct init_config));

    rgp = &xrs->rgp;
    INIT_LIST_HEAD(&rgp->node_list);
    INIT_LIST_HEAD(&rgp->pt_node_list);

    return xrs;
}

特に何もしていません。

xrs.h によると、

/*
 * xrs_init() - Register resource solver. Resource solver client needs
 *              to call this function to register itself.
 *
 * @cfg:   The system metrics for resource solver to use
 *
 * Return: A resource solver handle
 *
 * Note: We should only create one handle per AIE array to be managed.
 */
void *xrs_init(struct init_config *cfg);

とありました。

おわり

AMD XDNAが PCI Device となっていることがわかりました。

関連ブログ

vengineer.hatenablog.com

vengineer.hatenablog.com

vengineer.hatenablog.com

vengineer.hatenablog.com

vengineer.hatenablog.com

vengineer.hatenablog.com