|
[导读] 最近使用ZYNQ做一个高速数据采集,需要访问一个ADI的高速模数采样芯片,该芯片是利用三线制实现读以及写的功能。三线制实现写通信或许大家都经常会这样用,三线制实现读/写或许有的朋友就未曾这样用过。今天就分享一下利用现成IP不写任何代码怎么实现三线制SPI。; ]; A: k: P( q! z3 F/ p5 A1 _5 x 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;assign phy_cs = spi_cs;* X) t! E/ ?3 N% m. k- D; w assign phy_mosi = spi_sdo_t ? 1'bz : spi_sdo; assign spi_sdi = spi_three_wire ? phy_mosi : phy_miso;+ o4 b' Y; @2 ]0 P8 t6 U 比如,我是这样写的: `timescale 1ns / 100ps& M1 l1 V5 K, ~3 {. S; k//顶层设计文件 module system_top (1 P& O- C9 K* V' a- y! {2 A7 Z //DDR信号 inout [14:0] ddr_addr, inout [ 2:0] ddr_ba,' _- q$ o: c: [5 N* R0 ~' J2 P6 b inout ddr_cas_n, inout ddr_ck_n, inout ddr_ck_p,/ j4 V9 I! ^: f0 U0 p* Z* K* T inout ddr_cke,4 x1 V' ]; h9 q& r4 |- c inout ddr_cs_n,$ T5 R2 V* W/ b k1 y- P inout [ 3:0] ddr_dm, inout [31:0] ddr_dq,. y) e3 K( X: ~; N& \ inout [ 3:0] ddr_dqs_n, inout [ 3:0] ddr_dqs_p,) f9 r4 h0 y! c; X7 m inout ddr_odt, inout ddr_ras_n, inout ddr_reset_n, inout ddr_we_n, //必须的一些PS信号 inout fixed_io_ddr_vrn, inout fixed_io_ddr_vrp, //54个PS MIO引脚 inout [53:0] fixed_io_mio,$ b4 M+ K7 B( ?/ F, R: L //PS时钟引脚 inout fixed_io_ps_clk,' O; ]2 z; P- D; e% A //PS上电复位信号+ `$ U, |2 s2 r( n, S inout fixed_io_ps_porb, //PS SRSTB信号/ X! L9 i- k% Z0 c1 T inout fixed_io_ps_srstb,( `' a; K" m, {# F9 F7 o" I& n output [1:0] spi_0_cs,5 }7 R" l. u6 M5 o$ k+ x- h output spi_0_sclk, input spi_0_sdi, output spi_0_sdo,2 e/ H" ^$ { [7 K4 Q ); % u: T6 T$ Z8 ]% r7 j. O! i wire ip_spi_0_cs; wire ip_spi_0_sclk; wire ip_spi_0_sdi;3 b6 V" S2 E6 l: d2 p A$ X wire ip_spi_0_sdo; wire ip_spi_0_three_wire; wire ip_spi_0_sdo_t;2 O8 n' W. M J( l C assign spi_0_cs = ip_spi_0_cs; assign spi_0_sclk = ip_spi_0_sclk;' S0 L$ E4 Y4 S4 L( D //如此处理,这样引出的SPI可以兼容3线制以及4线制SPI8 Z& n" b# f: Z H/ N4 f% Z assign spi_0_sdo = ip_spi_0_sdo_t ? 1'bz : ip_spi_0_sdo; assign ip_spi_0_sdi = ip_spi_0_three_wire ? spi_0_sdo : spi_0_sdi; $ E8 O! U/ [2 b$ |' C; F //例化block设计3 L5 l. n( g& t ip_block_wrapper i_system_wrapper ( //DDR以及常规MIO、时钟、复位等信号! X8 ?, h6 E; L' J2 g .DDR_addr(ddr_addr),! _. N$ }6 j+ j0 S$ a .DDR_ba(ddr_ba),* e9 t3 j$ j# c, g8 O .DDR_cas_n(ddr_cas_n), .DDR_ck_n(ddr_ck_n),/ o2 S, |/ h0 H M2 B: C/ F .DDR_ck_p(ddr_ck_p), .DDR_cke(ddr_cke), .DDR_cs_n(ddr_cs_n),6 W( k6 x# S/ C .DDR_dm(ddr_dm), .DDR_dq(ddr_dq), .DDR_dqs_n(ddr_dqs_n), .DDR_dqs_p(ddr_dqs_p),: u4 B$ v6 _# Y! L* { .DDR_odt(ddr_odt), .DDR_ras_n(ddr_ras_n), .DDR_reset_n(ddr_reset_n), .DDR_we_n(ddr_we_n),3 n! X) S" T5 x8 h .FIXED_IO_ddr_vrn(fixed_io_ddr_vrn), .FIXED_IO_ddr_vrp(fixed_io_ddr_vrp), .FIXED_IO_mio(fixed_io_mio),. B7 u2 U% [+ e4 ~6 M+ c+ M8 L .FIXED_IO_ps_clk(fixed_io_ps_clk),* f# Y. o' m) K, ~0 | .FIXED_IO_ps_porb(fixed_io_ps_porb),, @4 u3 A, E& w- S! j .FIXED_IO_ps_srstb(fixed_io_ps_srstb), * E6 Z2 N# E) i- C9 n+ Y# r, L 2 L3 o- U3 D& ?5 R //连至顶层 .spi_0_cs(ip_spi_0_cs), .spi_0_sclk(ip_spi_0_sclk),! e! ^% {8 f& S8 | .spi_0_sdi(ip_spi_0_sdi),3 Q: C5 n- e* z .spi_0_sdo(ip_spi_0_sdo), .spi_0_sdo_t(ip_spi_0_sdo_t),; \3 V# `' @9 L+ ~7 ?, { .spi_0_three_wire(ip_spi_0_three_wire) 4 B/ k4 t; H; K+ R# D ); endmodule Linux端配置 首先需要配置设备树: &axi_spi_engine_0 {- t6 I: s, G. r* h5 N) m5 mstatus = "okay"; //配置SPI控制器匹配字段,这样会自动编译ADI 提供的SPI 控制器驱动' Z, { c& _' [* K compatible = "adi,axi-spi-engine-1.00.a"; spi-rx-bus-width = <1>;$ r- |; T& ^! U& j spi-tx-bus-width = <1>; " H! U9 N% C: p z6 K( V4 l0 P) Q bits-per-word = <8>; interrupt-parent = <&intc>; interrupts = <0 30 4>; num-cs = <4>; #address-cells = <0x1>;* Z6 G8 v: w, c: G7 w- `7 y) ` #size-cells = <0x0>; ad9467_0: ad9467@0 { compatible = "adi,ad9467";% U; K* ^! ^8 u3 c4 e3 U7 _ reg = <0>;$ h7 t2 N4 L# q0 E spi-max-frequency = <500000>; #address-cells = <1>;% r2 n- z0 X7 o. s #size-cells = <1>; 9 o, i) q5 d8 E9 a spi-rx-bus-width = <1>; , ~, [) U1 p7 d0 ^" p spi-tx-bus-width = <1>;' K. j6 Z8 a; W0 |! o bits-per-word = <8>; spi-3wire; 1 @$ a$ a% P; l2 ^+ H- L# |6 Q) R& n };) ~3 g* o6 [, D! N% F9 K ad9467_1: ad9467@1 {$ r n+ G# v. @ compatible = "spidev";5 b q! o; l! E. R5 V+ M reg = <1>; spi-max-frequency = <500000>; #address-cells = <1>; #size-cells = <1>; ' i* y6 _1 b/ O% b" D) k$ z. O 5 E R# R3 g6 C$ g3 b1 c spi-rx-bus-width = <1>; 2 F/ S* `2 z& e; t spi-tx-bus-width = <1>; bits-per-word = <8>;6 c; V9 `' x7 Y# f4 o7 K$ [$ u* {! d# b //这个字段需要使能,表示该设备是三线制' m' {) ^& _* s+ u3 C5 K: J spi-3wire; 2 _ M0 m5 V% ~9 Z }; 7 T7 s, e* V. c" b, q }; 基于ADI提高的Linux代码库: https://github.com/analogdevicesinc/Linux 配置相应的SPI控制器驱动,然后编译部署。由于该demo涉及些项目其他的技术细节,这里就不描述了,来看看疗效:
看这个波形或许会有朋友问:为啥数据发送结束,SDO/SDI复用脚波形有一个上升的渐变暂态过程,这是由于此时从端芯片从输出态转为高阻态,而主端芯片此时也是高阻态,由于线路电容效应故而会有这样一个变化过程。 总结一下利用ADI提高的IP库,不用敲一行代码可以很容易就实现了三线制SPI,香吧?该方案可以同时兼容三线制/四线制SPI,是一个成熟稳定的方案。为什么ZYNQ芯片这样一款SOC芯片以及Linux会被人喜欢,由此可见一斑。因为有大量成熟的轮子可供使用,而不必自己去造轮子。从而加速产品的研发进度,使用户可以专注于自己需要解决的应用问题。这里有一个tips拿走不谢:
|
微信公众号
手机版