使用官方的HDL试验DAC输出 之二

73 篇文章 11 订阅
25 篇文章 50 订阅

上一个BLOG分析了ADC输出的ENABLE,VALID,DATA信号。补充一张在旁边呜呜响的频谱分析仪的图:

 

我们看到中心频率是2.4G,在2.4G技术上加了大约半个,每格式2M,半格就是1M。这是因为我们使用的是2.4G射频,基带上DDS出了一个1M的正弦波,又因为发射端是上变频,射频加基带的DDS频率。所以就显示成了2.4g+1m的样子。

咱们在上一个BLOG里面看的波形频率是1M,这里还有个TX_SAMPLE_RATE一般都等于RX_SAMPLE_RATE,在这例子中都是37M,就是用来画一个1MHZ正弦波的时钟(可见37点画一个周期的正弦波),也就是上个BLOG中ILA的采样时钟。这个时钟在发射出去的射频层面看不出来。

 

以上是题外话,写这些BLOG是要在上一篇的基础上继续顺着数据往下分析:4路16BIT(有用的只有12BIT)看看是如何组合成一个64位的数据。(这个64位的数据之后被送入ADC_DMA,之后被自动打入到DDR3内存)。

 

这里我们看VALID SYNC 和ADC_DATA这三个信号。这部分其实开动想象力猜猜就大约知道什么意思:从AD9361收到的数据经过FIFO进行时钟域跨越后输入给此模块。我们上述又分析了这些数据可能是2个16位也可能是4个16位(分别对应1R1T和2R2T),而这些两种情况都要打入到ADC_DMA模块传输,首先要转换成64位。当2R2T时,很显然是四个数据直接并列开来就是4*16,而1R1T时候则需要两周期的数据合并成一个64BIT的数据。这个合并的就是PACK,就是这个模块做的事情。

下图是2R2T模式下抓到的ILA波形:

下图是1R1T模式下抓到的ILA波形:

(这里还发现数据是先填充低32位再填充高32位)

我们分析这个模块的代码,感觉写的配置行和适应性都很强。可配置性是说输入输出位宽,通道数都可以配置,适应性说任意的ENBALE和VALID组合都照顾到了。其实我们这里看到ENABLE和VALID进行AND操作后,无非是1R1T下的4'B1100和 2R2T下的4'B1111.

这里我们看到VALID信号用来做下游FIFO的WRITE_ENABLE是完全足够的,为什么还要一个SYN呢,这里有点看不明白的是SYN信号的话我们就要分析ADC_DMA模块。

 

看到这个FIFO接口确实有FIFO_wR_SYNC这个信号。应该是一个很重要很有意义的信号。我们进入IP核内部分析。

在IP核里面一层一层找到最终SYN起作用的代码。层级如下图:

 

最后找到了的代码让我哭笑不得:

这里就是为了和VALID信号进行AND操作以确保打入数据的有效。但是,这里有一个很大的但是就是100行,直接一个needs_sync=0将这个信号否定了,导致了在唯一出现的101行代码的has_sync线用于是1.

SYNC终于也搞明白了。

我们注意这个ADC_DMA和不错的地方是他的FIFO写入口支持时钟输入。这就省掉了进行时钟区域穿越的处理,而直接使用基带时钟将采集的数据打入ADC_DMA中。

另外顺便提一下,在接收通路用到了一个双时钟FIFO进行时钟区域的穿越,两个时钟分别是RX_CLK 和BB_CLK(BaseBand Clock 基带时钟)。

 

既然都分析到这里了,那就直接写个模块替代掉util_pack这个模块,我们现在还有一点不确定就是2路16位数据是如何组成一个32位的数据,两个32位数据组成64位我们从图中看显然是先来的那个在低32位。为了搞明白2路16位数据是如何组成一个32位的数据,我们就再接一个逻辑分析仪在PACK这个模块的输入和输出。

之后运行一下看结果:

1R1T截图:

 

显然这和明显了,在组成32位时候,DATA0和DATA2放在低16位。

我们再看看2R2T模式,是否也遵循这个规律。

OK 很显然也遵循这个规律。因此有了上面分析我们写出下面的代码可以替换掉util_pack模块。


module my_pack (
input rstn ,
input valid0,valid1,valid2,valid3,
input enable0,enable1,enable2,enable3,
input [16:0]din0,din1,din2,din3,
output reg res_valid,
output [63:0]  res_q
);

wire mode_1R1T = {enable0,enable1,enable2,enable3} ==4'b1100;
reg f;always@(posedge clk or negedge rstn) if (!rstn)  f<=0;else f<=!f ;
reg [63:0]  q_mode_1R1T;
reg  valid_mode_1R1T;
always @(posedge clk) if (f==0) q_mode_1R1T[31:0]<= {din0,din1} ;
always @(posedge clk) if (f==1) q_mode_1R1T[32+31:32+0]<= {din2,din3} ;
always @(posedge clk) valid_mode_1R1T<=f==1; 



reg [63:0]  q_mode_2R2T;

always @(posedge clk)  q_mode_2R2T[31:0]<= {din1,din0} ;
always @(posedge clk)  q_mode_2R2T[32+31:32+0]<= {din3,din2} ;


always @ (posedge clk)  if (mode_1R1T)res_q <= q_mode_1R1T ;else res_q<=q_mode_2R2T;
always @ (posedge clk)  if (mode_1R1T)res_valid <=  valid_mode_1R1T;else res_valid <=1;


endmodule 

代码简单精简一下并做了简单修改如下,并且加入一个SYN输出信号(虽然没有用到):



module my_pack (
input rst , clk,
input enable0,enable1,enable2,enable3,
input [15:0]din0,din1,din2,din3,
output reg res_valid,
output res_syn,
output reg [63:0]  res_q
);
assign res_syn =1;
reg  mode_1R1T ;

always @(posedge clk)mode_1R1T <= {enable0,enable1,enable2,enable3} ==4'b1100;

reg f;always@(posedge clk)  f<=~f ;

reg [63:0]  r64;
reg  valid_mode_1R1T;
always @(posedge clk) if (f==0||mode_1R1T==0) r64[31:0]<= {din1[15:0],din0[15:0]} ;
always @(posedge clk) if (mode_1R1T==0) r64[32+31:32+0]<= {din3[15:0],din2[15:0]} ; 
else if (f==1) r64[32+31:32+0]<= {din1[15:0],din0[15:0]} ;

always @(posedge clk) valid_mode_1R1T<=f==1; 


always @ (posedge clk)  res_q<=r64; 
always @ (posedge clk)   res_valid <= (mode_1R1T)? valid_mode_1R1T:1;

endmodule 

这里简单啰嗦一句,第一种写法是先造出两种情况的输出结果,之后再根据是什么模式选择其中一个结果。第二种写法是在第一种写法的基础上进行了合并。如果上来就看第二种写法很难看明白思路。实际代码我们可以用第一种写法,可读性好一些。

写这个模块只是分析到这里了,随手写出来,虽然完全可以替换掉,没有必要替换掉原来的模块。

 

官方一些代码写得很复杂,分析比较麻烦,我们很多时候直接拿来用就好。需要分析某些特性了再加分析仪去观测以及查看源代码。

 

 

上图是我使用的ZEBOARD+AD9361,看上去有点脏了哈,用了好一段时间了。大家可以到我网店购买,这样可以获取我免费技术支持。网址是SYSCLK.TAOBAO.COM。

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值