对AD9361的数据接口延迟设置的分析和记录

通过参看AD9361的寄存器说明手册,收发数据线的时钟和数据DELAY 各自是16级可调

打印出了NO-OS代码写006,007号寄存器的操作:

Hello World

lw:reg[6]=0x04
lw:reg[7]=0x70
lw:ad9361_dig_tune_rx

lw:ad9361_dig_tune_delay
lw:reg[6]=0x00
lw:reg[6]=0x01
lw:reg[6]=0x02
lw:reg[6]=0x03
lw:reg[6]=0x04
lw:reg[6]=0x05
lw:reg[6]=0x06
lw:reg[6]=0x07
lw:reg[6]=0x08
lw:reg[6]=0x09
lw:reg[6]=0x0a
lw:reg[6]=0x0b
lw:reg[6]=0x0c
lw:reg[6]=0x0d
lw:reg[6]=0x0e
lw:reg[6]=0x0f
lw:reg[6]=0xff
lw:reg[6]=0xfe
lw:reg[6]=0xfd
lw:reg[6]=0xfc
lw:reg[6]=0xfb
lw:reg[6]=0xfa
lw:reg[6]=0xf9
lw:reg[6]=0xf8
lw:reg[6]=0xf7
lw:reg[6]=0xf6
lw:reg[6]=0xf5
lw:reg[6]=0xf4
lw:reg[6]=0xf3
lw:reg[6]=0xf2
lw:reg[6]=0xf1
lw:reg[6]=0xf0
lw:reg[6]=0x00
lw:reg[6]=0x01
lw:reg[6]=0x02
lw:reg[6]=0x03
lw:reg[6]=0x04
lw:reg[6]=0x05
lw:reg[6]=0x06
lw:reg[6]=0x07
lw:reg[6]=0x08
lw:reg[6]=0x09
lw:reg[6]=0x0a
lw:reg[6]=0x0b
lw:reg[6]=0x0c
lw:reg[6]=0x0d
lw:reg[6]=0x0e
lw:reg[6]=0x0f
lw:reg[6]=0xff
lw:reg[6]=0xfe
lw:reg[6]=0xfd
lw:reg[6]=0xfc
lw:reg[6]=0xfb
lw:reg[6]=0xfa
lw:reg[6]=0xf9
lw:reg[6]=0xf8
lw:reg[6]=0xf7
lw:reg[6]=0xf6
lw:reg[6]=0xf5
lw:reg[6]=0xf4
lw:reg[6]=0xf3
lw:reg[6]=0xf2
lw:reg[6]=0xf1
lw:reg[6]=0xf0
lw:reg[6]=0x00
lw:reg[6]=0x01
lw:reg[6]=0x02
lw:reg[6]=0x03
lw:reg[6]=0x04
lw:reg[6]=0x05
lw:reg[6]=0x06
lw:reg[6]=0x07
lw:reg[6]=0x08
lw:reg[6]=0x09
lw:reg[6]=0x0a
lw:reg[6]=0x0b
lw:reg[6]=0x0c
lw:reg[6]=0x0d
lw:reg[6]=0x0e
lw:reg[6]=0x0f
lw:reg[6]=0xff
lw:reg[6]=0xfe
lw:reg[6]=0xfd
lw:reg[6]=0xfc
lw:reg[6]=0xfb
lw:reg[6]=0xfa
lw:reg[6]=0xf9
lw:reg[6]=0xf8
lw:reg[6]=0xf7
lw:reg[6]=0xf6
lw:reg[6]=0xf5
lw:reg[6]=0xf4
lw:reg[6]=0xf3
lw:reg[6]=0xf2
lw:reg[6]=0xf1
lw:reg[6]=0xf0
lw:ad9361_dig_tune_delay wit BE_VERBOSE : 
SAMPL CLK: 61440000 tuning: RX
  0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:
0:# o o o # # # # # o o o o o # # 
1:o o o o o O o o o o o # # # # # 
lw:gen the final delay : lw:reg[6]=0x50
lw:exit from ad9361_dig_tune_delay
lw:ad9361_dig_tune_tx

lw:ad9361_dig_tune_delay
lw:reg[7]=0x00
lw:reg[7]=0x01
lw:reg[7]=0x02
lw:reg[7]=0x03
lw:reg[7]=0x04
lw:reg[7]=0x05
lw:reg[7]=0x06
lw:reg[7]=0x07
lw:reg[7]=0x08
lw:reg[7]=0x09
lw:reg[7]=0x0a
lw:reg[7]=0x0b
lw:reg[7]=0x0c
lw:reg[7]=0x0d
lw:reg[7]=0x0e
lw:reg[7]=0x0f
lw:reg[7]=0xff
lw:reg[7]=0xfe
lw:reg[7]=0xfd
lw:reg[7]=0xfc
lw:reg[7]=0xfb
lw:reg[7]=0xfa
lw:reg[7]=0xf9
lw:reg[7]=0xf8
lw:reg[7]=0xf7
lw:reg[7]=0xf6
lw:reg[7]=0xf5
lw:reg[7]=0xf4
lw:reg[7]=0xf3
lw:reg[7]=0xf2
lw:reg[7]=0xf1
lw:reg[7]=0xf0
lw:reg[7]=0x00
lw:reg[7]=0x01
lw:reg[7]=0x02
lw:reg[7]=0x03
lw:reg[7]=0x04
lw:reg[7]=0x05
lw:reg[7]=0x06
lw:reg[7]=0x07
lw:reg[7]=0x08
lw:reg[7]=0x09
lw:reg[7]=0x0a
lw:reg[7]=0x0b
lw:reg[7]=0x0c
lw:reg[7]=0x0d
lw:reg[7]=0x0e
lw:reg[7]=0x0f
lw:reg[7]=0xff
lw:reg[7]=0xfe
lw:reg[7]=0xfd
lw:reg[7]=0xfc
lw:reg[7]=0xfb
lw:reg[7]=0xfa
lw:reg[7]=0xf9
lw:reg[7]=0xf8
lw:reg[7]=0xf7
lw:reg[7]=0xf6
lw:reg[7]=0xf5
lw:reg[7]=0xf4
lw:reg[7]=0xf3
lw:reg[7]=0xf2
lw:reg[7]=0xf1
lw:reg[7]=0xf0
lw:reg[7]=0x00
lw:reg[7]=0x01
lw:reg[7]=0x02
lw:reg[7]=0x03
lw:reg[7]=0x04
lw:reg[7]=0x05
lw:reg[7]=0x06
lw:reg[7]=0x07
lw:reg[7]=0x08
lw:reg[7]=0x09
lw:reg[7]=0x0a
lw:reg[7]=0x0b
lw:reg[7]=0x0c
lw:reg[7]=0x0d
lw:reg[7]=0x0e
lw:reg[7]=0x0f
lw:reg[7]=0xff
lw:reg[7]=0xfe
lw:reg[7]=0xfd
lw:reg[7]=0xfc
lw:reg[7]=0xfb
lw:reg[7]=0xfa
lw:reg[7]=0xf9
lw:reg[7]=0xf8
lw:reg[7]=0xf7
lw:reg[7]=0xf6
lw:reg[7]=0xf5
lw:reg[7]=0xf4
lw:reg[7]=0xf3
lw:reg[7]=0xf2
lw:reg[7]=0xf1
lw:reg[7]=0xf0
lw:ad9361_dig_tune_delay wit BE_VERBOSE : 
SAMPL CLK: 61440000 tuning: TX
  0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:
0:# # # # # # # # # # # # # # # # 
1:# # # o o o o o o O o o o o o o 
lw:gen the final delay : lw:reg[7]=0x90
lw:exit from ad9361_dig_tune_delay
ad9361_init : AD936x Rev 2 successfully initialized

我们看到主要的操作都在 ad9361_dig_tune_delay里面实施的。

看看ad9361_dig_tune_delay的实现:


static int32_t ad9361_dig_tune_delay(struct ad9361_rf_phy *phy,
		uint32_t max_freq, enum dig_tune_flags flags, bool tx)
{
	static const uint32_t rates[3] = {25000000U, 40000000U, 61440000U};
	uint32_t s0, s1, c0, c1;
	uint32_t i, j, r;
	bool half_data_rate;
	uint8_t field[2][16];
	printf("lw:ad9361_dig_tune_delay\n");
	if (((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ||
	    !phy->pdata->rx2tx2))
	    half_data_rate = false;
	else
	    half_data_rate = true;

	memset(field, 0, 32);
	for (r = 0; r < (max_freq ? ARRAY_SIZE(rates) : 1); r++) {
		if (max_freq)
			ad9361_set_trx_clock_chain_freq(phy,
				half_data_rate ? rates[r] / 2 : rates[r]);

		for (i = 0; i < 2; i++) {
			for (j = 0; j < 16; j++) {
				/*
				 * i == 0: clock delay = 0, data delay from 0 to 15
				 * i == 1: clock delay = 15, data delay from 15 to 0
				 */
				ad9361_set_intf_delay(phy, tx, i ? 15 : 0,
						      i ? 15 - j : j, j == 0);
				field[i][j] |= ad9361_check_pn(phy, tx, 4);
			}
		}

		if ((flags & BE_MOREVERBOSE) && max_freq) {
			ad9361_dig_tune_verbose_print(phy, field, tx, -1, -1);
		}
	}

	c0 = ad9361_find_opt(&field[0][0], 16, &s0);
	c1 = ad9361_find_opt(&field[1][0], 16, &s1);

	if (!c0 && !c1) {
		ad9361_dig_tune_verbose_print(phy, field, tx, -1, -1);
		dev_err(&phy->spi->dev, "%s: Tuning %s FAILED!", __func__,
			tx ? "TX" : "RX");
		return -EIO;
	} else if (flags & BE_VERBOSE) {
		if (c1 > c0)
			ad9361_dig_tune_verbose_print(phy, field, tx, (s1 + c1 / 2), -1);
		else
			ad9361_dig_tune_verbose_print(phy, field, tx, -1, (s0 + c0 / 2));
	}
	printf("lw:gen the final delay : ");
	if (c1 > c0)
		ad9361_set_intf_delay(phy, tx, s1 + c1 / 2, 0, true);
	else
		ad9361_set_intf_delay(phy, tx, 0, s0 + c0 / 2, true);
	printf("lw:exit from ad9361_dig_tune_delay\n");
	return 0;
}

其实这里面的逻辑很简单,我们以006寄存器为例子,就是clk和data的先后延迟关系,

第一种情况是:将clk可以延迟0-15个单位(每个单位是大约是0.3ns)。

第一种情况是:将data可以延迟0-15个单位(每个单位是大约是0.3ns)。

这样就总共有32种情况了。

是否存在clk和data都设置延迟的情况,这实际是没有意义的。因为都延迟的话,实际有意义的仅仅是他们延迟的差值。具体而言,比如006设置为0XF9是就相当于:时钟CLK延迟15个单位,数据DATA延迟9个单位,那么他们之间的差值是6吧。就相当于0X60。

我们看到打印出来的代码设置了CLK的延迟是0和F这两种情况下列举例虽有DATA的延迟。

我们看到0X00....0X0F 很好理解,就是CLK不延迟,数据延迟0...15.

而看到0XFF...0XF0实际就是对应0X00...XF0。

 

其实有了32种情况以后确定一个合适的延迟数值的算法也是很明显和简单的:找几个连续的可用数值,取中间那个就OK。

比如:

 

SAMPL CLK: 61440000 tuning: RX
  0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:
0:# o o o # # # # # o o o o o # # 
1:o o o o o O o o o o o # # # # # 

lw:gen the final delay : lw:reg[6]=0x50

006 寄存器取的是0x50

 

0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:
0:# # # # # # # # # # # # # # # # 
1:# # # o o o o o o O o o o o o o 

007这里取的就是0X90

图中#是表示对应组合数据通路出错

o表示OK 没有错。O表示被选用的。我们看出O是位于多个o之间取一个中间数值。左右偏移5个数值(约和5*0.3ns)都是OK的。

 

不知道写这些代码的人是怎么想的,故意让代码看上去难以理解,这代码就是让人用,而不想让你学明白,从而保持对他们依赖。

查看打印出来的代码实际可以看到进行了三轮测试,某一延迟设置只要有一轮数据不对应就会设置对应filed为1,标志该位出错不能用。 

另外考虑到AD9361本身支持BIST各种测试和校验,自行编写一个设置寄存器的程序不是很难。

还有一个问题就是如果BBPLL或者一些滤波配置的一些参数设置进行了修改导致了SAMPLE 发生了变化,是否需要调整这个已经校准好的006 和007寄存器呢。带着这个问题我也做了一个实验:我做了LOGO文件,修改RX_SAMPLE_RATE和TX_SAMPLE_RATE后发现006和007寄存器没有被修改。另外从理论角度,修改此两个寄存器就是为了满足数据采集的RX_DATA,TX_DATA部分数据采集时间,这两个时间都是硬确定的,不以频率大小为转移的。而输出端的TCO时间也一个确定的数值,是由芯片内部布线延迟和逻辑延迟所确定的,再加上PCB上线的长度是确定,所以延迟也确定,因此这个数据确定就可以不变了。

进一步推论,这个数据实际可以在产品中却行下来,之后写死。你可能会说不通时候编译FPGA的结果也可能是会有差别。是的别忘记咱们得到的数据本身就是有很大荣誉的。从这个例子看,前后差别1ns(3个延迟单位)是没有问题的。这样就使得问题得到了简化:在设计时候时候我们让外设提供一个简单的输入006寄存器内容的方法,确定好一个稳定的中间值。之后在后续的程序里面直接使用这个数值就好。问题简化!

以上对006寄存器的讨论同样适用于寄存器007.

这里使用的平台是ZEDBOARD+AD9361。REG[006]=0X50,REG[007]=0X90这个配置应该是适合这个套硬件的。根据我们之上大胆的分析以后可以直接这样用。因此在使用纯PL生成SPI 接口的寄存器配置时候,可以直接设置.

如果使用配置界面,则配置如下图红框处:

 

以上!

 

 

 

 

  • 10
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
下面是一个简单的Verilog示例,展示了如何在FPGA上实现与AD9361数据接口通信。这个例子使用了AXI接口和SPI通信。 ```verilog module AD9361_Interface ( input wire clk, input wire rst, input wire [15:0] spi_data_in, output wire [15:0] spi_data_out, output wire spi_sclk, output wire spi_cs, inout wire spi_sdo, inout wire spi_sdi ); // AXI slave interface for data read/write reg [7:0] axi_slave_addr; reg [31:0] axi_slave_data_in; wire [31:0] axi_slave_data_out; wire axi_slave_rd; wire axi_slave_wr; // SPI controller reg spi_enable; reg [3:0] spi_bit_counter; // Register map for AD9361 localparam REG_CTRL = 8'h00; localparam REG_CONFIG = 8'h01; // Add more register addresses as needed // Initialize signals assign spi_data_out = axi_slave_data_out[15:0]; assign axi_slave_data_out[31:16] = 16'b0; assign spi_sclk = 1'b0; assign spi_cs = 1'b1; // SPI enable signal generation (you need to add your own logic to enable/disable SPI controller) always @(posedge clk or posedge rst) begin if (rst) begin spi_enable <= 1'b0; end else begin // Add your own logic here to enable/disable SPI controller // This could be based on a button press, a control signal, or any other condition spi_enable <= 1'b1; end end // SPI controller always @(posedge clk or posedge rst) begin if (rst) begin spi_bit_counter <= 4'b0; end else begin if (spi_enable) begin spi_bit_counter <= spi_bit_counter + 1; if (spi_bit_counter == 4'b0) begin spi_sdo <= 1'b0; // Initialize SDI low spi_cs <= 1'b0; // Assert CS spi_sclk <= 1'b1; // Start clock high end else if (spi_bit_counter == 4'b1) begin spi_sdo <= axi_slave_rd ? 1'b0 : spi_data_in[15]; // Send MSB of data spi_sclk <= 1'b0; // Clock low end else if (spi_bit_counter >= 4'b2 && spi_bit_counter <= 4'b17) begin spi_sdo <= axi_slave_rd ? 1'b0 : spi_data_in[15 - (spi_bit_counter - 2)]; // Send next bit of data spi_sclk <= ~spi_sclk; // Toggle clock end else if (spi_bit_counter == 4'b18) begin spi_sdo <= axi_slave_rd ? 1'b0 : 1'bz; // Send dummy bit or tri-state SDI spi_sclk <= ~spi_sclk; // Toggle clock end else if (spi_bit_counter == 4'b19) begin spi_sdi <= 1'bz; // Release SDO (tri-state) spi_sclk <= 1'b1; // Stop clock high end else if (spi_bit_counter >= 4'b20 && spi_bit_counter <= 4'b35) begin spi_data_out[15 - (spi_bit_counter - 20)] <= spi_sdi; // Receive next bit of data spi_sclk <= ~spi_sclk; // Toggle clock end else if (spi_bit_counter == 4'b36) begin spi_data_out[15] <= spi_sdi; // Receive LSB of data spi_cs <= 1'b1; // Deassert CS spi_enable <= 1'b0; // Disable SPI controller spi_sclk <= 1'b1; // Stop clock high end end else begin spi_sdo <= 1'bz; // Tri-state SDI spi_sdi <= 1'bz; // Tri-state SDO spi_cs <= 1'b1; // Deassert CS spi_sclk <= 1'b1; // Stop clock high end end end // AXI slave interface for data read/write always @(posedge clk or posedge rst) begin if (rst) begin axi_slave_addr <= 8'h0; axi_slave_data_in <= 32'b0; end else begin if (axi_slave_wr) begin case(axi_slave_addr) REG_CTRL: begin // Logic to handle write to control register (e.g., enable/disable AD9361) // You can use axi_slave_data_in to read the control data from AXI bus // and perform necessary actions to control AD9361. end REG_CONFIG: begin // Logic to handle write to configuration register (e.g., set AD9361 configuration) // You can use axi_slave_data_in to read the configuration data from AXI bus // and perform necessary actions to configure AD9361. end // Add more cases for other register addresses default: begin // Handle write to an unsupported register address end endcase end else if (axi_slave_rd) begin case(axi_slave_addr) REG_CTRL: begin // Logic to handle read from control register (e.g., read status of AD9361) // You can update axi_slave_data_out to provide the status data to AXI bus. end REG_CONFIG: begin // Logic to handle read from configuration register (e.g., read AD9361 configuration) // You can update axi_slave_data_out to provide the configuration data to AXI bus. end // Add more cases for other register addresses default: begin // Handle read from an unsupported register address axi_slave_data_out <= 32'h0; // Provide default value for unsupported address end endcase end end end endmodule ``` 上述代码是一个简单的AD9361数据接口FPGA程序示例,使用AXI接口和SPI通信进行数据读写。请根据实际需求和使用的FPGA平台,对代码进行适当的修改和调整。 需要注意的是,这只是一个基本的框架,您需要根据AD9361的寄存器映射和配置需求,实现适当的逻辑来读写寄存器以及与AD9361进行通信。另外,还需要根据您所使用的FPGA平台和工具链,配置引脚映射和约束文件,以确保正确的信号连接和时序约束。 建议参考ADI提供的参考设计、文档和例程,以更好地了解AD9361数据接口和FPGA实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值