关于如何在Cyclone V SoC FPGA系统中为HPS添加一个SPI外设,这个实验我已经进行了有相当长的一段时间了,但直到今日方才有所突破,顺利的完成了SPI控制器的添加,驱动加载和基本测试。主要是因为自己对Linux方面知之甚少。
这里先简要介绍自己的操作过程,然后说明自己遇到的问题,再记录自己解决问题的方法。
在一个现成的Qsys系统中,例如友晶的DE1-SoC开发板的DE1_SOC_Linux_FB工程,打开Qsys,在Qsys系统中添加一个SPI外设,设定想要的SCLK时钟频率,例如这里设置为2M,实际上得到的应该是1.92M。将其salve端口连接到h2f_lw_axi_master上。导出相应的管脚,添加到Quartus的顶层文件中。
这里需要将SPI的四个信号作为顶层管脚导出。实际使用时我并未分配实际的管脚,因为这仅仅算是我盲目探索过程,所以没有做完整的系统。但是测试时候是需要验证的,怎么办呢,这里我使用Signaltap直接抓取这几个引脚的信号,通过查看引脚的波形就能大致判断是否能正确的使用该SPI核了。例如我最终抓取到的测试波形如下图所示。当然了,这是后话,成功之后才能看到这个波形的,之前一直是不识别SPI设备,也就无法编程控制了。
添加好SPI控制器之后需要全编译Quartus工程,并生成dts和dtb文件,dts文件是设备树,是用来供Linux读取硬件信息并对应加载设备驱动的,dtb文件是dts编译之后的二进制格式。这里我直接在SOCEDS command shell中将路径切换到工程目录下,输入make dts来生成dts文件,然后输入make dtb命令来生成dtb文件。
生成dts的时候很顺利,没有遇到什么困难,但是在生成dtb的时候,遇到了如下报错:
提示dts文件中有错误,pll_stream这个节点不存在。这个IP我知道,是生成一个时钟供FrameReader读取数据的,其并没有连接到HPS的总线上,也不需要受HPS控制,因此这个地方即使有错误也不会影响系统正常的运行。因此,参考提示信息说的方法,使用该-f参数来强制输出dtb文件。命令为:dtc -I dts -fo soc_system.dtb soc_system.dts。这样就能够正确的生成dtb文件了。
生成dtb文件后,将其重命名为socfpga.dtb,并拷贝到开发板的启动SD卡中。
然后将工程编译得到的sof文件转换得到rbf文件,命名为soc_system.rbf,也拷贝到开发板的启动SD卡中。这个rbf文件就是包含了有spi控制器的soc系统的FPGA部分的配置数据。该数据会在HPS启动的时候配置到FPGA中。
以上只是完成了第一步,接下来,要想Linux系统能够自动的加载SPI驱动,需要配置Linux内核以使能该SPI控制器的驱动编译。
使能之后,保存配置并编译得到内核镜像文件zImage文件。将zImage同样拷贝到开发板的启动SD卡中。
这样,从理论上来说,为HPS添加SPI控制器和驱动支持的工作就算是完成了,接下来只需要将SD卡插入SOC开发板中,上电启动,芯片就会自动引导加载,并运行Linux操作系统,通过dtb读取到支持的SPI控制器,并识别到HPS总线上挂载的altera SPI控制器,为其注册驱动。然后当Linux系统启动完成后,就可以在/dev下看到spi设备了。
但是实际上,当我按照这个流程操作之后,确实在系统启动的打印信息中看到了SPI控制器已经被识别,如下所示:
但是在Linux的/dev路径下,却死活找不到SPI设备。网友帮忙分析说是设备确实已经识别了,但是可能dts中缺少一些描述信息,没能匹配上驱动信息,因此没有成功的加载驱动,创建设备。
正当自己想破脑袋也不知道怎么办时,网友发过来一个连接,是一个博主的博客,为表尊重,这里附上其博文链接。https://blog.csdn.net/u013625961/article/details/56282731
在这个博文里,有这样一段话:
以下为博客原文:
修改device tree的描述
spi_0: spi@0x100000100 {
compatible = "altr,spi-16.1", "altr,spi-1.0";
reg = <0x00000001 0x00000100 0x00000020>;
interrupt-parent = <&hps_0_arm_gic_0>;
interrupts = <0 43 4>;
clocks = <&clk_0>;
#address-cells = <1>; /* embeddedsw.dts.params.#address-cells type NUMBER */
#size-cells = <0>; /* embeddedsw.dts.params.#size-cells type NUMBER */
bus-num = <0>; /* embeddedsw.dts.params.bus-num type NUMBER */
num-chipselect = <1>; /* embeddedsw.dts.params.num-chipselect type NUMBER */
status = "okay"; /* embeddedsw.dts.params.status type STRING */
spidev0: spidev@0 {
compatible = "rohm,dh2228fv"; /* appended from boardinfo */
reg = <0>; /* appended from boardinfo */
spi-max-frequency = <1000000>; /* appended from boardinfo */
}; //end spidev@0 (spidev1)
}; //end spi@0x100000100 (spi_0)
其中#address-cells = <1>;#size-cells = <0>; 两行必须用。
spi-max-frequency = <1000000>; 这个信息在设备注册时必须使用,否则会导致注册不成功。1000000 clock frequency要符合。
博客原文结束。
意思是需要我们手动修改dts文件的内容,既然要手动修改,我就对自动生成的dts文件的内容和这个修改后的内容的差别产生了兴趣,于是打开自动生成的dts文件中的内容进行对比,对比如下所示:
左侧的是博主的修改后的内容,而右侧的是SoC EDS软件自动生成的dts文件,可以看到,自动生成的内容里缺少了很多信息。
既然这样,就先按照博主的说明,将这部分原本没有的内容手动添加进自动生成的dts文件的相应位置,然后使用该dts重新编译得到dtb文件,并替换掉SD卡中的dtb文件。然后重新启动开发板。
只是在添加这部分内容的时候,同样也遇到了一点小插曲,我最开始是直接将这部分内容复制粘贴到我的dts文件中的。然后编译时会报如下错误:
ERROR (duplicate_label): Duplicate label 'spidev0' on /sopc@0/spi@0xfff00000/spidev@0 and /sopc@0/bridge@0xc0000000/spi@0x1000100e0/spidev@0
意思是说有两个spidev@0设备,冲突了。好吧,那是哪里冲突了呢?在dts文件中尝试以spidev@0作为关键词搜索,发现文件中有两个地方涉及到了spidev@0,第一处是我刚刚添加的地方,第二处在HPS中,是HPS中的硬核SPI控制器。如下图所示:
既然这样,那就自己手动将SPI_0下面的spidev0节点修改巍峨spidev1,保存后再次执行编译dtb操作,就能够成功的生成dtb文件了,将新的dtb文件拷贝到sd卡中替换之前的dtb文件。然后重启开发板,当系统启动之后,在/dev路径下就能看到一个名为spidev32766.0的设备了,编写一个简单的C语言测试程序,对该设备执行简单的读写测试,程序源码如下所示:
然后拷贝到系统中执行,通过signaltap抓取SPI信号线上的波形,确实能够看到标准的SPI传输过程。
至此,终于完成了SPI控制器的添加和使用,困扰我一个多月的问题总算是解决了。(PS:我这一个月可不是因为这个问题卡住了别的啥事都不干哈,只是因为遇到困难,每天事情又太多,就暂时搁置了。)
附:今天看到前两天写的一篇博文被网友评论了,对方应该是个专家,所以指出了我文章中存在的很多问题,说的很有道理,一瞬间感觉自己挺“白”的,挺打击积极性的,虽然话说的很难听,不过还是很感谢他,让我意识到了自己的很多不足,由于他提出的问题确实值得深思,所以我暂时将文章删除了,待日后好好学习论证之后,补充完善了再发吧。虽然写了两三年博客,遇到过各种评论,很多评论也挺打击积极性的,不过心态还是要好,坚持写作,不断的提升自己。
用户3880491 2018-5-9 23:32