|
导读:最近使用ZYNQ做一个高速数据采集,需要访问一个ADI的高速模数采样芯片,该芯片是利用三线制实现读以及写的功能。三线制实现写通信或许大家都经常会这样用,三线制实现读/写或许有的朋友就未曾这样用过。今天就给大家分享一下,利用现成IP不写任何代码如何实现三线制SPI。 ADI很多芯片都采用三线制SPI进行控制,以AD9467为例,AD9467是一款 pipeline架构16位高速ADC芯片,采样率高达250MSPS。在一些复杂系统中其应用领域比较广泛:
从芯片框图,大致可以看出,该芯片主要由以下部分组成:
对于芯片的其他部分,不是本文介绍的重点,这里来看看其SPI的通信时序图:
结合SPI模式时序图:
故,CPOL=0,CPHA=0. 另外,第一个bit用于标识本次报文你发起的是读还是写操作,这种设计是不是有点类似I2C标准中的读写位? 柳暗花明那么问题来了,我们需要做的SPI通信需要实现三线制SPI进行读以及写:
经过一番折腾,在ADI官方发现了一个宝藏:
https://wiki.analog.com/resources/fpga/peripherals/spi_engine 官方实现了SPI engine IP 框架:
其verilog HDL代码库位于: https://github.com/analogdevicesinc/hdl.git PS/PL设计下好hdl库,按照向导将库make,执行对应的tcl脚本,生成了hdl库相应所需文件。然后按照需要设计以下block设计:
然后在顶层设计文件进行例化,这里问题来了,spi_1还是4个脚,如果就这样拉出到PL端的引脚上,还是四线制,那么该怎么改呢? 看看wiki中图以及描述,发现需要还需要在转一下:
从而添加如下代码在顶层文件: assign phy_sclk = spi_sclk;3 t, |0 n% { e7 R4 k3 O; m5 {assign phy_cs = spi_cs;7 j! X" d6 t; v3 H: `. G6 S a) h assign phy_mosi = spi_sdo_t ? 1'bz : spi_sdo; assign spi_sdi = spi_three_wire ? phy_mosi : phy_miso;4 |0 w" d) Q" k8 g 比如,我是这样写的: `timescale 1ns / 100ps* F' t" ]& C* v7 z/ P9 e//顶层设计文件 module system_top ( //DDR信号; k$ ]7 Y4 ]/ ^2 } inout [14:0] ddr_addr,& N! U I, J8 g H' `0 U2 w9 f inout [ 2:0] ddr_ba, inout ddr_cas_n,: q. O5 b, s+ L' h, [+ T; Z inout ddr_ck_n, j. m2 f6 x" r4 F4 W$ k0 b inout ddr_ck_p, inout ddr_cke, `; l; Z% r1 p$ L* g inout ddr_cs_n,# O) X* Q( o6 K0 S inout [ 3:0] ddr_dm, inout [31:0] ddr_dq,- S0 C& k, } e% f5 ~& q5 ` inout [ 3:0] ddr_dqs_n,. U7 X. ?! W2 v inout [ 3:0] ddr_dqs_p, inout ddr_odt,# f9 \) K: A. B- [ inout ddr_ras_n, inout ddr_reset_n, inout ddr_we_n, //必须的一些PS信号 inout fixed_io_ddr_vrn,: j" V# g& Q) d O6 u; z6 j5 r inout fixed_io_ddr_vrp,5 Y: J& x! c# M4 Z2 U6 P //54个PS MIO引脚' M; Q" Y0 S. ] inout [53:0] fixed_io_mio,. V% j' L6 K5 V; Q9 u //PS时钟引脚 inout fixed_io_ps_clk, //PS上电复位信号+ D. r) ~3 x- u$ M. I inout fixed_io_ps_porb,, }; B6 ]+ C1 |1 v //PS SRSTB信号 inout fixed_io_ps_srstb, output [1:0] spi_0_cs,8 Y3 E2 B+ n9 \# ^* n7 u$ s8 v- a% n3 T output spi_0_sclk,* {! L! w1 q& u/ g( P input spi_0_sdi, output spi_0_sdo, ); ! ?- n+ |+ o4 D8 C wire ip_spi_0_cs; wire ip_spi_0_sclk;& d: L+ i4 O, r1 P! W wire ip_spi_0_sdi;5 D! i6 {+ w$ V3 B wire ip_spi_0_sdo;5 G, o$ l2 K2 a- \, [1 r wire ip_spi_0_three_wire; ) B; i: \* h# U: m wire ip_spi_0_sdo_t; - p' C, m0 ^" ?. ^8 J assign spi_0_cs = ip_spi_0_cs; assign spi_0_sclk = ip_spi_0_sclk; //如此处理,这样引出的SPI可以兼容3线制以及4线制SPI assign spi_0_sdo = ip_spi_0_sdo_t ? 1'bz : ip_spi_0_sdo;3 P5 l3 [, L( ]+ r. _ assign ip_spi_0_sdi = ip_spi_0_three_wire ? spi_0_sdo : spi_0_sdi;0 f, t5 u) |* N# d //例化block设计+ l& G4 G- ?! k* v! H ip_block_wrapper i_system_wrapper ( //DDR以及常规MIO、时钟、复位等信号9 X1 O0 ^; n! |9 q .DDR_addr(ddr_addr),6 r, D4 h$ ~: ^' w .DDR_ba(ddr_ba), .DDR_cas_n(ddr_cas_n),0 x" N) O8 _5 N5 @9 R0 h .DDR_ck_n(ddr_ck_n), .DDR_ck_p(ddr_ck_p),6 `0 s9 q: b+ @3 R5 J/ E" l- G8 d .DDR_cke(ddr_cke), .DDR_cs_n(ddr_cs_n), .DDR_dm(ddr_dm), |. w% {! H( `6 Z/ h ]# O .DDR_dq(ddr_dq),4 ^. W. A7 g' j+ `/ D .DDR_dqs_n(ddr_dqs_n),: A- C; ?4 W) y7 X0 i' R: I .DDR_dqs_p(ddr_dqs_p), .DDR_odt(ddr_odt),$ k; X& `/ O2 N5 n, d4 ^ .DDR_ras_n(ddr_ras_n),( ?5 L/ @! V7 y# ^2 E .DDR_reset_n(ddr_reset_n), .DDR_we_n(ddr_we_n), .FIXED_IO_ddr_vrn(fixed_io_ddr_vrn), .FIXED_IO_ddr_vrp(fixed_io_ddr_vrp),+ m8 V8 k% J' ]. R% F3 V .FIXED_IO_mio(fixed_io_mio),) h2 R& m7 \& N7 \7 R .FIXED_IO_ps_clk(fixed_io_ps_clk), .FIXED_IO_ps_porb(fixed_io_ps_porb), .FIXED_IO_ps_srstb(fixed_io_ps_srstb), //连至顶层 .spi_0_cs(ip_spi_0_cs),' G/ |( C* P2 `# P .spi_0_sclk(ip_spi_0_sclk),: J7 u3 Z; q1 ` .spi_0_sdi(ip_spi_0_sdi), .spi_0_sdo(ip_spi_0_sdo),) Z& @) i8 A4 E' f _9 r# O .spi_0_sdo_t(ip_spi_0_sdo_t), .spi_0_three_wire(ip_spi_0_three_wire) + r0 W- i0 N( Q2 O% i5 m- u# T ); endmodule% i" y& m" f/ m% x3 W2 W! k Linux端配置 首先需要配置设备树: &axi_spi_engine_0 {status = "okay"; //配置SPI控制器匹配字段,这样会自动编译ADI 提供的SPI 控制器驱动& ?, k" q' x% q3 u( } compatible = "adi,axi-spi-engine-1.00.a"; spi-rx-bus-width = <1>; spi-tx-bus-width = <1>; bits-per-word = <8>;# K: w) T$ g4 A7 q interrupt-parent = <&intc>;" P+ o6 W2 q6 |. z! R# @& b interrupts = <0 30 4>; ) d! I: H2 Q; {$ `& m k' g+ O% A num-cs = <4>; #address-cells = <0x1>;! R, t4 ^' t& C! H# _ #size-cells = <0x0>; ; p% [2 h4 }( n: J) Q: Q ad9467_0: ad9467@0 {: p1 w' k9 O; K2 V% g, ` compatible = "adi,ad9467"; reg = <0>;" ^" N( O! s6 x$ L2 q spi-max-frequency = <500000>; #address-cells = <1>;( g B d5 o" t3 I; s4 z# B9 Q #size-cells = <1>;" z+ m& i Y; J6 ] spi-rx-bus-width = <1>; , E! K V( e6 J4 C. m spi-tx-bus-width = <1>; bits-per-word = <8>;7 y+ C: T+ ^7 C spi-3wire; , V# q* W" P. ], T9 Z7 V7 o }; : \) K6 p% e5 l9 m0 h) w) L' I) g ad9467_1: ad9467@1 { compatible = "spidev"; reg = <1>;; p2 @/ u4 c- K spi-max-frequency = <500000>;- \1 O7 w' K: ` #address-cells = <1>; #size-cells = <1>; spi-rx-bus-width = <1>; 7 j5 W' a$ D$ X6 T; `; W2 ~ spi-tx-bus-width = <1>;6 J* c. ?$ x$ k7 d5 w bits-per-word = <8>; //这个字段需要使能,表示该设备是三线制7 T) t+ d' m7 T" v3 f4 D spi-3wire; : r( C7 K/ g5 w9 |, H( }! {" T }; 2 a6 q/ N; J( T+ v };4 A# L# W: o+ t1 N h5 T# S( X 基于ADI提高的Linux代码库: https://github.com/analogdevicesinc/Linux 配置相应的SPI控制器驱动,然后编译部署。由于该demo涉及些项目其他的技术细节,这里就不描述了,来看看疗效:
看这个波形或许会有朋友问:为啥数据发送结束,SDO/SDI复用脚波形有一个上升的渐变暂态过程,这是由于此时从端芯片从输出态转为高阻态,而主端芯片此时也是高阻态,由于线路电容效应故而会有这样一个变化过程。 总结一下利用ADI提高的IP库,不用敲一行代码可以很容易就实现了三线制SPI,香吧?该方案可以同时兼容三线制/四线制SPI,是一个成熟稳定的方案。为什么ZYNQ芯片这样一款SOC芯片以及Linux会被人喜欢,由此可见一斑。因为有大量成熟的轮子可供使用,而不必自己去造轮子。从而加速产品的研发进度,使用户可以专注于自己需要解决的应用问题。这里有一个tips拿走不谢:
|
微信公众号
手机版