christ0127

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

FPGA通常工作在MHz,而串口波特率远远低于该频率(最高标准速率115200),所以我们需要想办法生成一个滴答时钟来尽可能的接近串口波特率。这里我们用串口链路的最高速度来举例说明:

先来啰嗦一段

在典型的串口设计中,RS-232芯片通常使用1.8432MHz的时钟,因为这样比较容易生成串口的标准波特。1.8432MHz除以16得到115200Hz。当然,也有直接使整个系统工作在11.0592MHz的时钟方案。

// 假设FPGA时钟信号是1.8432MHz
// 设计一个4-bit计数器
reg [3:0] BaudDivCnt;
always @(posedge clk) 
    BaudDivCnt <= BaudDivCnt + 1; // count forever from 0 to 15

// 滴答时钟(每16个时钟滴答一次,即115200次每秒)
wire BaudTick = (BaudDivCnt==15);

是不是很简单?
但是如果你的时钟不是1.8432MHz,比如是2MHz,怎么办?为了从2MHz时钟得到115200Hz, 我们需要除以17.361111111...,然而这并不是一个整数,FPGA也表示不出来。解决的办法是:有时候我们用17来除,有时候用18来除,确保最后的比例是17.361111111即可,这样实现起来就容易多了:

先来看C代码:

while(1) // repeat forever
{
  acc += 115200;
  if(acc>=2000000) 
    printf("*"); 
  else 
    printf(" ");

  acc %= 2000000;
}

代码的执行结果是以一个特定的比率打印"*",这个比率就是17.361111111...。

为了在FPGA上得到相同的效果,有一个很重要的前提,那就是:我们的串口是可以接受波特率在一定范围内产生偏差的,即115200Hz ± x%。

如果我们的时钟频率刚好是2的幂次方就最好了,但是显然2MHz不是。为了在FPGA上好做一点,我们需要用一个近似值来代替2000000/115200,这里以1024/59 = 17.356为例。 这已经很接近我们想要的17.361111...了。

在FPGA上可以这样实现:设定一个10-bit的累加器,累加步长为59,把它的溢出信号作为滴答信号即可:

// 假设FPGA工作在2.0000MHz
// 一个11-bit累加器,最高位用作累加器的溢出信号
reg [10:0] acc;   // 11 bits

// 每次自加59
always @(posedge clk)
  acc <= acc[9:0] + 59; 

wire BaudTick = acc[10]; // 溢出信号作为滴答信号

在2MHz时钟频率下,每秒滴答115234次,相比于理想波特率115200,其误差为0.03%,这是可以接受的。

参数化的FPGA波特率发生器:

前面的例子我们用到了10 bit的累加器,但是随着时钟频率的增加,显然我们需要使用更多的比特。

我们再以25MHz时钟为例,设置一个16 bit累加器,并且将其参数化

parameter ClkFreq = 25000000; // 25MHz
parameter Baud = 115200;
parameter BaudGenAccWidth = 16;
parameter BaudGenInc = (Baud<<BaudGenAccWidth)/ClkFreq;

reg [BaudGenAccWidth:0] BaudGenAcc;
always @(posedge clk)
  BaudGenAcc <= BaudGenAcc[BaudGenAccWidth-1:0] + BaudGenInc;

wire BaudTick = BaudGenAcc[BaudGenAccWidth];

如果你按照上面的方法去做了,细心的同学会发现这里是有问题的。因为verilog使用了32bit的中间结果,而我们在计算BaudGenInc显然溢出了。解决办法如下:

parameter BaudGenInc = ((Baud<<(BaudGenAccWidth-4)) + (ClkFreq>>5)) / (ClkFreq>>4);
posted on 2016-05-20 11:11  christ0127  阅读(1302)  评论(0编辑  收藏  举报