簡體   English   中英

Linux Kernel 設備驅動寫入回調是在整個分配給設備驅動的 memory 空間中寫入數據

[英]Linux Kernel Device Driver Write Callback is writing data across the entire allocated memory space for the device driver

我有一個 Xilinx SoC,並通過 verilog 在可編程邏輯上創建了一個簡單的乘法器。 乘法器接受兩個 16 位輸入,將它們相乘並返回一個 32 位 output。 數字設計已通過 AXI-Lite 接口封裝並鏈接到 SoC 內的處理器系統。 Xilinx 工具已為此數字設計自動生成設備樹實體,因此可以創建自定義 linux 設備驅動程序以與數字設計交互(即 PS 會將其視為連接到 ARM 處理器的外部硬件設備) .

生成的設備樹如下所示:

/ {
    amba_pl: amba_pl@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges ;
        multi2_0: multi2@a0000000 {
            clock-names = "s00_axi_aclk";
            clocks = <&zynqmp_clk 71>;
            compatible = "xlnx,multi2-1.0";
            reg = <0x0 0xa0000000 0x0 0x10000>;
            xlnx,s00-axi-addr-width = <0x4>;
            xlnx,s00-axi-data-width = <0x20>;
        };
    };
};

所以從設備樹中我們可以看到乘法器(“multi2-1.0”)的物理memory地址為0xa0000000,地址寬度為0x4,數據寬度為32位。

因此,從設備驅動程序的角度來看,特別是在寫回調 function 中,我正在將一個 32 位數字寫入從“ioremap(.)”檢索到的虛擬 memory 地址 ZC1C4245268E67A94D11

進行了完整性檢查以查看虛擬 memory 映射到物理地址,並且它似乎正確完成,沒有錯誤(驅動程序中的一些與內存相關的代碼片段如下所示):

struct simpmod_local {
    int irq;
    unsigned long mem_start;
    unsigned long mem_end;
    void __iomem *base_addr;
};
struct simpmod_local *lp = NULL;

……

static int simpmod_probe(struct platform_device *pdev)
{ .....
lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
...
dev_info(dev,"simpmod at 0x%08x mapped to 0x%08x, irq=%d\n",
        (unsigned int __force)lp->mem_start,
        (unsigned int __force)lp->base_addr,
        lp->irq); 
....
}

寫回調 function(截至目前)只是取一個 32 位數字並將其放入 memory。 但是,即使我從 base_address+0x20_offset 讀取,讀取回調 function 也只是讀取完全相同的數字。 我嘗試更改偏移值,但無論如何,它一直在讀取相同的數字。

我的直覺告訴我,如果從不同的 memory 地址讀取該值應該是垃圾值或零,但它不太可能讀取寫入基地址的相同值。 為什么要在整個分配的 memory 空間中復制寫入的數據?

即使執行 devmem 命令 <devmem 0xa0000000 w 52> 也會在執行 <devmem 0xa0000020 w> 或 <devmem 0xa0000040 w> 或......

寫回調 function 如下所示:

static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off){
  sscanf (buf,"%d,%d",&operand_1,&operand_2);
  ker_buf[len] = 0 ;
  iowrite32((unsigned int) operand_1, lp->base_addr);
  return len;
}

完整的項目代碼(稍作改動)可在https://forums.xilinx.com/t5/Embedded-Linux/Memory-Replications-during-write-call-back-function-in-Linux/mp/1212405上找到

警告:這與其說是一個解決方案,不如說是一些觀察和嘗試的事情[無特定順序]。

目前,您有多個潛在的錯誤來源:硬件邏輯錯誤、設備驅動程序錯誤。

從鏈接的驅動程序代碼中,大多數return語句返回錯誤代碼(例如-ENOMEM ),但有些確實return -1 這是不一致的。

正如我在評論中提到的,你有很多全局變量。 沒有線程間鎖定。 所以,你可能有競爭條件。

我想你正在啟動petalinux 而且,只要您不訪問您的設備,它就可以工作。 這是一件大事[以一種好的方式]。

我假設您通過串行電纜從您的開發系統 [運行(例如) minicom ] 到板載 UART 與它通信。 因此,您會收到登錄提示和/或 shell。

這意味着 UART 驅動程序源 [和相應的 dtb/dts] 可用。 您可以將其用作參考驅動程序。 或者,像 GPIO 之類的其他東西。

我注意到您提到了ZYNQ [這是一種相當流行的 Xilinx FPGA 芯片]。 我假設您也在使用帶有ZYNQ芯片的標准 SDK 板。 因此,Vivado 已經了解了電路板互連/布局。

而且,我假設 Vivado 能夠將板定義傳遞給 Xilinx 的 S/W SDK/builder,以便它可以構建兼容的petalinux kernel。

我從未見過寫入值並正確讀取它,但在整個 memory 中復制了該數據。

這意味着您設備中的地址匹配邏輯不僅響應其分配的地址范圍,而且響應更多不應該的地址。 可能與其他設備重疊,它們可能正在競爭/競賽。

不是Vivado 專家,但是...

從您的鏈接中,查看 Vivado windows 之一的.png ,它說AXI BASEADDR0xFFFFFFFFAXI HIGHADDR0x00000000 兩者都有一個藍色的i

這些對我來說非常可疑,因為我認為這些值應該與 DTB 條目中的值匹配。 而且, BASEADDR值對我來說毫無意義。

我想知道是否可以將 DTB 生成到某個健全的地址,但生成的實際硬件邏輯是不同的。

這很容易導致您看到的所有症狀。

可能有幫助的一件事是將chipscope添加到硬件設計中,以便您可以調試硬件邏輯和/或觀察對給定端口/地址范圍的任何訪問。

您正在使用copy_to_user等。 人。 但是,這可能會失敗,並且您沒有檢查錯誤代碼。 我還會對正在傳遞的 arguments 進行printk

無法保證傳遞給dev_read/dev_writelen值足以包含傳輸大小。 dev_read中,您執行ioread32 但是,你這樣做: int n = sprintf(ker_buf, "%d\n", read_val); 沒有檢查nlen以確保有足夠的空間。 而且,您不是在檢查/尊重loff_t

這兩個函數都傳遞了一個struct file指針。 但是,這個值會被忽略,取而代之的是您已經設置的全局變量。 正如我在頂級評論中提到的那樣,使用這些全局變量是有問題的。 您應該使用傳遞的指針來找到適當的struct指針和 [最終] 您的私有設備 struct simpmod_local

您的dev_write應該將來自用戶空間的值存儲到私有結構中。 dev_read應該從那里得到它們。

這是一個總的猜測:我見過的大多數設計都使用完整的AXI而不是AXI lite。 我對“AXI 線程 ID”的構成一無所知,所以我不知道您的訪問代碼在內核之間彈跳的含義可能是什么 [如果有的話]。

像你一樣使用dev_write/dev_read不是原子的。 我認為,目前,你有更根本的問題。 但是,從長遠來看,我會用一個帶有structioctl調用來替換它,例如:

struct mymult_user {
    u32 operand_1;
    u32 operand_2;
    u32 result;
};

ioctl調用對此進行了copy_from_user 將這些值發送到 H/W,取回結果。 並且,將結果返回給 ioctl 調用者。 或者,它可以在structresult字段上執行copy_to_user

總體而言,您更有可能在 Xilinx 的論壇頁面上獲得 [有用的] 響應 [因為一直在做這些事情的人經常光顧它]。


更新:

我注意到了別的東西。

DTB 條目將 AXI 數據寬度指定為 0x20。 這是32字節?? 它是自動生成的,所以它必須是正確的;-) 但是,這對我來說似乎太過分了。 它可能只是與 AXI 數據總線的寬度有關,所以,也許不是問題......

但是,查看驅動程序,基地址的偏移量似乎不匹配。

operand_1是偏移量0x10, operand_2是偏移量0x20,結果是偏移量0x30。 那么,偏移量 0x0 是什么?

AXI 總線的寬度和寄存器的寬度可能沒有嚴格的關系。

一種查看方式是偏移量應與總線寬度對齊:0x0、0x20、0x40。

但是,通常情況下,我希望事情會更加緊湊。 (例如)分別偏移 0x0、0x2、0x4。

在調試時只做ioread*可能不會那么痛苦[減少內存/總線損壞的機會]。 由於您沒有寫入地址空間,因此它不太可能損壞其他 memory 單元,並且系統可能會更長時間地保持 [未損壞]。 這只會給你最初結果 reg 中的任何值。

此外,您可以在ioread32上編寫操作數和循環以獲取偏移量(例如) printk並打印這些值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM