fpga4fun.com - where FPGAs are fun.
Home
Welcome
Information


FPGA projects
Music box
Pong game
R/C servos
Text LCD module
Quadrature decoder
PWM and one-bit DAC
Debouncer
LED displays
Crossing clock domains
External contributions

FPGA interface projects
RS-232
JTAG
I2C
EPP
SPI
CNC steppers

FPGA advanced projects
Graphic LCD panel
Digital oscilloscope
10BASE-T interface
PCI interface
Spoc CPU core

Hands-on
A simple oscilloscope


FPGA introduction
What are FPGAs?
How FPGAs work
FPGA pins
Clocks and global lines
Download cables
Configuration
Learn more

FPGA software
Design software
Pin assignment
Design-entry/HDL
Simulation/HDL
Synthesis and P&R

FPGA electronic
SMD technology
Crystals and oscillators

HDL info
HDL tutorials
Verilog tips
VHDL tips

Quick-start guides
ISE
Quartus

Site
News
FPGA links
HDL tutorials
Forum


Baud generator

Here we want to use the serial link at maximum speed, i.e. 115200 bauds. Other slower speeds would also be easy to generate.

FPGAs usually run at speed well above 115200Hz (RS-232 is pretty slow by today's standards). That means we use a high-speed clock and divide it down to generate a "tick" as close as possible to 115200 times a second.

Synchronous tick from a 1.8432MHz clock

Traditionally, RS-232 chips use a 1.8432MHz clock, because that makes generating the standard baud frequencies very easy. So let's assume we have a 1.8432MHz clock available.

1.8432MHz divided by 16 gives 115200Hz, what a coincidence!

reg [3:0] BaudDivCnt;
always @(posedge clk) BaudDivCnt <= BaudDivCnt + 1;

wire BaudTick = (BaudDivCnt==15);

So "BaudTick" is asserted once every 16 clocks, i.e. 115200 times a second when using a 1.8432MHz clock.

Synchronous tick from any frequency

The earlier generator was assuming the use of a 1.8432MHz clock. But what do you do if all your have is, say, a 2MHz clock? To generate 115200Hz from a 2MHz clock, you divide the clock by "17.361111111..." Not exactly a round number. The solution is to divide sometimes by 17, sometimes by 18, making sure the ratio stays "17.361111111". That's actually easy to do.

Look at the following "C" code:

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

  acc %= 2000000;
}

That prints the "*" in the exact ratio, once every "17.361111111..." loops on average.

To obtain the same thing efficiently in an FPGA, we rely on the fact that the serial interface can tolerate a few % of error in the baud frequency generator. It really won't matter if we use "17.3" or "17.4".

FPGA baud generator

It is desirable that the 2000000 be a power of two. Obviously 2000000 is not a power of two. So we change the ratio. Instead of the ratio "2000000/115200", let's use "1024/59" = 17.356. That's very close to our ideal ratio, and makes an efficient FPGA implementation.

// 10 bits for the accumulator ([9:0]), and one extra bit for the accumulator carry-out ([10])
reg [10:0] acc;   // 11 bits total!

always @(posedge clk)
  acc <= acc[9:0] + 59; // use only 10 bits from the previous result, but save the full 11 bits

wire BaudTick = acc[10]; // so that the 11th bit is the carry-out

Using our 2MHz clock, "BaudTick" is asserted 115234 times a second, a 0.03% error from the ideal 115200.

Parameterized FPGA baud generator

The previous design was using a 10 bits accumulator, but as the clock frequency increases, more bits are required.

Here's a design with a 25MHz clock and a 16 bits accumulator. The design is parameterized, so easy to customize.

parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;
parameter BaudGeneratorAccWidth = 16;
parameter BaudGeneratorInc = (Baud<<BaudGeneratorAccWidth)/ClkFrequency;

reg [BaudGeneratorAccWidth:0] BaudGeneratorAcc;
always @(posedge clk)
  BaudGeneratorAcc <= BaudGeneratorAcc[BaudGeneratorAccWidth-1:0] + BaudGeneratorInc;

wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth];

One last implementation issue: the "BaudGeneratorInc" calculation is wrong, due to the fact that Verilog uses 32 bits intermediate results, and the calculation exceeds that. Change the line as follow for a workaround.

parameter BaudGeneratorInc = ((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4);

This line has also the added advantage to round the result instead of truncating.

That's it.
Now that we have a precise enough Baud generator, we can go ahead with the RS-232 transmitter and receiver modules.



>>> NEXT: RS-232 transmitter module >>>



This page was last updated on February 15 2008.