我有一个USB设备输出大小为一个字节的数据,我想将这些字节传递给AXI网桥上存在的FPGA组件,FPGA和CPU在同一芯片上...它是SoC FPGA Altera Cyclone V CPU是ARM Cortex-A9。内核版本3.7.0。
有一个软件可以从USB设备读取并写入转储文件......它可以正常工作。我尝试使用mmap()
将FPGA地址映射到虚拟空间并从用户空间写入。这样做......说完一分钟后,内核似乎崩溃了。
我为我的FPGA组件编写了一个驱动程序,并将驱动程序路径作为文件传递给该软件,以便它写入它,最终写入我的FPGA组件,但结果相同......内核在崩溃后再次崩溃随机时间。
我还编写了一个简单的程序,从本地文件中读取字节并将其传递给FPGA ...这种方法很好(使用mmap()
或驱动程序模块),文件传递到FPGA,没有无论文件有多大,都会出现问题。
所以问题是当从USB设备传递到FPGA时,使用mmap()
或驱动程序模块。
以下是一个示例崩溃消息:
Internal error: Oops - undefined instruction: 0 [#1] SMP ARM
Modules linked in: ipv6
CPU: 1 Not tainted (3.7.0 #106)
PC is at scheduler_ipi+0x8/0x4c
LR is at handle_IPI+0x10c/0x19c
pc : [<800521a0>] lr : [<800140d4>] psr: 80000193
sp : bf87ff58 ip : 8056acc8 fp : 00000000
r10: 00000000 r9 : 413fc090 r8 : 00000001
r7 : 00000000 r6 : bf87e000 r5 : 80535018 r4 : 8053eec0
r3 : 8056ac80 r2 : bf87ff58 r1 : 00000482 r0 : 00000481
Flags: Nzcv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 10c5387d Table: 3f0c404a DAC: 00000015
Process swapper/1 (pid: 0, stack limit = 0xbf87e240)
Stack: (0xbf87ff58 to 0xbf880000)
ff40: 00000000 800140d4
ff60: fffec10c 8053e418 bf87ff90 fffec100 8000f6e0 8000851c 8000f708 8000f70c
ff80: 60000013 ffffffff bf87ffc4 8000e180 00000000 00000000 00000001 00000000
ffa0: bf87e000 80565688 803ddfb0 80541fc8 8000f6e0 413fc090 00000000 00000000
ffc0: 8053e9b8 bf87ffd8 8000f708 8000f70c 60000013 ffffffff 00000020 8000f894
ffe0: 3f86c06a 00000015 10c0387d 805658d8 0000406a 003d1ee8 31ca2085 5c1021c3
Code: eaffffad 80564700 e92d4800 e1a0200d (4c4c9b50)
---[ end trace 9e492cde975c41f9 ]---
其他崩溃消息的开头如下:
Unable to handle kernel paging request at virtual address 2a7a4390
Internal error: Oops - bad syscall: ebcffb [#1] SMP ARM
pgd = bf318000
[2a7a4390] *pgd=00000000
和
Internal error: Oops - undefined instruction: 0 [#2] SMP ARM
Modules linked in: ipv6
CPU: 1 Tainted: G D (3.7.0 #106)
以下是完整的crash messages。
我注意到我得到的所有崩溃消息都与PC和LR位置相交,但实际上我没有Linux内核的经验。我在网上发现了类似的错误消息,但提议的解决方案都没有对我有用。
源代码:
只要新的字节缓冲区从USB到达,就会调用此函数:
static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
{
if (ctx) {
if (do_exit)
return;
if ((bytes_to_read > 0) && (bytes_to_read < len)) {
len = bytes_to_read;
do_exit = 1;
rtlsdr_cancel_async(dev);
}
/* if (fwrite(buf, 1, len, (FILE*)ctx) != len) {
fprintf(stderr, "Short write, samples lost, exiting!\n");
rtlsdr_cancel_async(dev);
}
*/
if (fm_receiver_addr == NULL)
{
virtual_base = mmap(NULL, HPS2FPGA_SPAN, PROT_WRITE, MAP_PRIVATE, fd, HPS2FPGA_BASE);
if (virtual_base == MAP_FAILED)
{
perror("mmap");
close(fd);
exit(1);
}
fm_receiver_addr = (unsigned char*)(virtual_base + FM_DEMOD_OFFSET);
}
int i, j;
for (i = 0; i < len; i++)
{
*fm_receiver_addr = buf[i];
for (j = 0; j < 150; j++);
}
if (bytes_to_read > 0)
bytes_to_read -= len;
}
}
您看到我评论了fwrite()
函数(它由原始代码用于写入文件)并将其替换为我写入FPGA组件的代码:*fm_receiver_addr = buf[i];
。在此之前,我检查地址以查看它是否有效,如果不是,则检查另一个地址。
另一方面,驱动程序模块,我写了这段代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#include <linux/io.h>
#define HPS2FPGA_BASE 0xC0000000
#define HPS2FPGA_SPAN PAGE_SIZE
void* fm_demod_addr;
int i;
// Get a driver entry in Sysfs
static struct device_driver fm_demod_driver =
{
.name = "fm-demodulator", // Name of the driver
.bus = &platform_bus_type, // Which bus does the device exist
};
// Function that is used when we read from the file in /sys, but we won't use it
ssize_t fm_demod_read(struct device_driver* drv, char* buf)
{ return 0; }
// Function that is called when we write to the file in /sys
ssize_t fm_demod_write_sample(struct device_driver* drv, const char* buf, size_t count)
{
if (buf == NULL)
{
pr_err("Error! String must not be NULL!\n");
return -EINVAL;
}
for (i = 0; i < count; i++)
{
iowrite8(buf[i], fm_demod_addr);
}
return count;
}
// Set our module's pointers and set permissions mode
static DRIVER_ATTR(fm_demod, S_IWUSR, fm_demod_read, fm_demod_write_sample);
// Set module information
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Siraj Muhammad <sirajmuhammad@outlook.com>");
MODULE_DESCRIPTION("Driver for FPGA component 'FM Demodulator'");
static int __init fm_demod_init(void)
{
int ret;
struct resource* res;
// Register driver in kernel
ret = driver_register(&fm_demod_driver);
if (ret < 0)
return ret;
// Create file system in /sys
ret = driver_create_file(&fm_demod_driver, &driver_attr_fm_demod);
if (ret < 0)
{
driver_unregister(&fm_demod_driver);
return ret;
}
// Request exclusive access to the memory region we want to write to
res = request_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN, "fm-demodulator");
if (res == NULL)
{
driver_remove_file(&fm_demod_driver, &driver_attr_fm_demod);
driver_unregister(&fm_demod_driver);
return -EBUSY;
}
// Map the address into virtual memory
fm_demod_addr = ioremap(HPS2FPGA_BASE, HPS2FPGA_SPAN);
if (fm_demod_addr == NULL)
{
driver_remove_file(&fm_demod_driver, &driver_attr_fm_demod);
driver_unregister(&fm_demod_driver);
release_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN);
return -EFAULT;
}
return 0;
}
static void __exit fm_demod_exit(void)
{
// Remove file system from /sys
driver_remove_file(&fm_demod_driver, &driver_attr_fm_demod);
// Unregister the driver
driver_unregister(&fm_demod_driver);
// Release requested memory
release_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN);
// Un-map address
iounmap(fm_demod_addr);
}
module_init(fm_demod_init);
module_exit(fm_demod_exit);
我将用户空间代码恢复到其原始状态,并将驱动程序路径:/sys/bus/platform/drivers/fm-demodulator/fm_demod
传递给用户空间应用程序以写入它。
有没有想过呢?
答案 0 :(得分:2)
Internal error: Oops - undefined instruction: 0 [#1] SMP ARM
PC is at scheduler_ipi+0x8/0x4c
LR is at handle_IPI+0x10c/0x19c
pc : [<800521a0>] lr : [<800140d4>] psr: 80000193
[snip]
Code: eaffffad 80564700 e92d4800 e1a0200d (4c4c9b50)
---[ end trace 9e492cde975c41f9 ]---
没有人可以完全知道答案。注意:未定义指令!
PC位于 scheduler_ipi + 0x8 / 0x4c ,这是硬核ARM-Linux调度;处理器间中断。您可以反汇编代码:&#39;部分来帮助,
0: eaffffad b 0xfffffebc
4: 80564700 subshi r4, r6, r0, lsl #14
8: e92d4800 push {fp, lr}
c: e1a0200d mov r2, sp
10: 4c4c9b50 mcrrmi 11, 5, r9, ip, cr0
崩溃发生在指令mcrrmi
,这似乎是无意义的。如果您反汇编 sched / core.o ,您将看到指令序列,但我敢打赌&#39; 4c4c9b50&#39;价值是腐败的。即,这不是编译器生成的代码。
所以问题是当从USB设备传递到FPGA时,使用
mmap()
或驱动程序模块。
我将使用禅动并思考一下。 USB设备使用DMA?您的FPGA可能也是如何控制ARM / AXI总线的。我至少会考虑FPGA可能会破坏总线周期并可能翻转地址位并导致对内核代码空间进行物理写入。当您使用无辜的副标识符(如DMA外设)时,可能会发生这种情况。 ARM CPU将使用缓存并突发所有内容。
要检查的事项,
您还应该为任何代码生成反汇编程序,并查看哪些寄存器有效。例如,可以找到(4c4c9b50)代码,
printf&#39; \ x50 \ x9b \ x4c \ x4c&#39; &GT; arm.bin
objdump -marm -b binary -D arm.bin
您可以objdump vmlinux
找到scheduler_ipi
例程的汇编程序,然后确定指针可能是什么。例如,如果this_rq()
位于R9
且R9
是假的,那么您就有了线索。
如果代码已损坏,您需要一个总线分析器和/或一些例程来监控位置并在其发生变化时报告以尝试找到损坏的来源。