本帖最后由 ilove314 于 2009-12-19 15:15 编辑
基于FPGA的跨时钟域信号处理——MCU说到异步时钟域的信号处理,想必是一个FPGA设计中很关键的技术,也是令很多工程师对FPGA望而却步的原因。但是异步信号的处理真的有那么神秘吗?那么就让特权同学和你一起慢慢解开这些所谓的难点问题,不过请注意,今后的这些关于异步信号处理的**里将会重点从工程实践的角度出发,以一些特权同学遇到过的典型案例的设计为依托,从代码的角度来剖析一些特权同学认为经典的跨时钟域信号处理的方式。这些**都是即兴而写,可能不会做太多的分类或者归纳,也有一些特例,希望网友自己把握。
另外,关于异步时钟域的话题,推荐大家不妨去看看这些不错的**,如《跨越鸿沟:同步世界中的异步信号》,《cpu与fpga跨时钟域数据交换的实现问题》等。
首先说MCU与FPGA之间的异步通信,参加CPLD助学活动的朋友应该都注意到了那块BJ-EPM板子上预留了16PIN的单片机接口,但是那个实验里其实也没有给出什么实验代码。究其原因,大概是特权同学有点自私了吧(呵呵~~~),因为当初刚接触MCU与FPGA通信处理的时候,是为了做一个液晶控制板,用的是很老的EPM7128,资源很小,摸索了个把月才搞定,不过当时的处理方式上并不稳妥,后来随着不断学习不断积累经验才寻觅到现在的处理方式。不想公开源码自有所谓的“比较关键的技术”一说,现在想来蛮有些可笑的。网络这么大一个平台,凭什么你只索取不共享呢?所以,特权同学今后会努力把自己的点点滴滴设计经验和大家分享。当然了,在提出自己的观点和看法的同时,也一定会得到更多高人不同的也许更好的见解,帮助他人的同时自己也在进步,何乐而不为呢。
罗嗦了一大堆,步入正题吧……
首先,这个项目是基于单片机的应用,如果你对单片机的读写时序不是很熟练,不妨看看特权同学的一篇详细讨论51单片机扩展RAM读写时序的**《单片机的扩展RAM读写时序》。下面简单看下11.0592MHz的51单片机的读写时序图吧。
大体和上面的波形相差无几,地址总线没有画出来,不过地址总线一般是会早于片选CS到来,并且晚于片选信号CS撤销(这个说法不是绝对的,但是至少对于下面的应用是这样)。
我们现在的工作是作为MCU的从机,即模拟MCU的扩展RAM。MCU若发出写时序,FPGA就得在数据稳定于数据总线时将其锁存起来;MCU发出读时序,FPGA就要在MCU锁存数据的建立时间之前把数据放到数据总线上,并且到MCU锁存数据的保持时间结束后才能将数据撤销。基本上,我们要干的就是这些活,下面讨论verilog在设计上如何实现,但是限于篇幅,不对时序分析做讨论,假定这是一个很理想的总线时序。
其实这个MCU的读写时序的时间相对还是很充裕的,因为我们的FPGA用的是50MHz的晶振。所以一个很基本的想法是要求我们把MCU端的信号同步到FPGA的时钟域上,达到异步信号的同步处理。
verilog代码:
//----------------------------------------------------------------------
//----------------------------------------------------------------------
input clk;
//50MHz
input rst_n;
//复位信号,低有效
input mcu_cs_n;
//MCU片选信号,低有效
input mcu_wr_n;
//MCU写信号,低有效
input[3:0] mcu_addr;
//MCU地址总线
input[7:0] mcu_db;
//MCU数据总线
reg[3:0] mcu_addr_r;
//mcu_addr锁存寄存器
reg[7:0] mcu_db_r;
// mcu_db锁存寄存器
//////mcu_cs_n和mcu_wr_n同时拉低时wr_state拉低,表示片选并写选通
wire
wr_state = mcu_cs_n || mcu_wr_n;
//写状态标志位,写选通时拉底
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
mcu_addr_r <= 4’h0;
mcu_db_r <= 8’h00;
end
else if(!wr_state) begin
mcu_addr_r <= mcu_addr;// mcu_addr锁存寄存器
mcu_db_r <= mcu_db;// mcu_db锁存寄存器
end
wire pos_wr;
// MCU写状态上升沿标志位
reg wr1,wr2;
// MCU写状态寄存器
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
wr1 <= 1'b1;
wr2 <= 1'b1;
end
else begin
wr1 <= wr_state;
wr2 <= wr1;
end
assign pos_wr = ~wr2 && wr1;
//写选通信号上升沿pos_wr拉高一个时钟周期
|