Day 3 of making a UART š² I made a baud rate generator
I want to try a different approach for this article, instead of simply explaining my implementation and reasoning, Iād like to explore the topic first. Iām working on improving my problem-solving skills by following the practices outlined in this article. My first order of business will be to break down the topic to a point where itās easily digestible. Down to first principles. Which means a fundamental truth, one that doesnāt need any additional information or facts to prove its validity. āFire is hotā for instance is not something to be debated, it just is, regardless of any external factors. Only then will I move on to explaining how Iāve implemented it, so letās get started!
What are we trying to achieve?
Before trying to understand the what and diving into too much detail, Iād like to define the desired outcome first. This allows us to have a clear idea of what the final result should look like, and derive a set of actions from there. The purpose of a baud rate generator in our case (making a UART) is to determine communication speed. Being that the A in UART stands for asynchronous, meaning that the devices donāt have to share a clock signal, we know that we canāt just rely on system clocks being the same to synchronize transmission and reception. + that would limit portability.
Letās look at the importance of agreeing on a set speed of communication: If I have one device transmitting at 20 bit/s and the other receiving at 10 bit/s Iām only gonna get 50% of the data! Itās fairly simple to understand now, why we need a way to coordinate our efforts and make sure weāre on the same page between devices.
The benefits of having baud rate
Iāve already covered the main benefit, which is that with the same baud rate weāre sure to not miss any data (this is actually not true, weāre only improving the odds of data reception but thereās no guarantee it will arrive to the reception device, I cover this later in the article). But I wanted to make this little section for those of us (like me) who sometimes need a bit more of a verbose explanation to understand something.
Letās say you have device A weāll call it Tx, the transmitter and device B which weāll call Rx, the receiver.
Weāll create a struct in pseudo-code for each of them and look at the benefits of a baud rate from a different perspective:
struct Tx {
SYSTEM_CLOCK_FREQ: u32 = 50000000; // 50 MHz
BAUD_RATE: u32 = 115200;
DIVIDER: u32 = SYSTEM_CLOCK_FREQ / BAUD_RATE; // 434
}
struct Rx {
SYSTEM_CLOCK_FREQ: u32 = 100000000; // 100 MHz
BAUD_RATE: u32 = 115200;
DIVIDER: u32 = SYSTEM_CLOCK_FREQ / BAUD_RATE; // 868
}
As we can see by using the same baud rate, we effectively make the devices match each other because the divider compensates for the different clock speeds. This is the beauty of the baud rate generatorāit abstracts away the differences in system clock speeds and ensures that the baud signal remains consistent across devices. Which might seem counterintuitive because the DIVIDER
value ends up being smaller on the Tx
device than the one in Rx
.
So it must go faster the Rx
DIVIDER
because we toggle the signal more often right? Well no, because even though the baud signal needs less clock cycles in Tx
to toggle, we have to remind ourselves that its clock is of a slower frequency, which means each clock cycle takes more time to complete. Therefore we end up with both devices transmitting and receiving at the same speed!
P.S. Baud rate Bd and bit rate bit/s arenāt always interchangeable but for our intents and purposes, they are. So letās not waste too much time on it.
My implementation
Letās see what it looks like in code:
Port declaration
module baudUnit (
input clk,
// input reset,
output reg baud,
output reg sample
);
Iāve commented out the reset
signal because Iām feeling conlficted about it. On one hand itās a common practice and is very useful for testing and/or shutting down a signal. But Iām not sure what the purpose of it would be since our top_module needs the baud. Add to that the fact that itās a bad practice to stop clock-like signals anyways because it messes with everything else. With the UART being dependent on the baud rate I truly do not see the point in having a reset port. That being said I recognize that perhaps I havenāt thought long enough about this so there might be some faults in my judgement. Iād be more than happy to receive some feedback and debate why it could be a good idea to keep it in.
Besides that, as we can see we have a very short list of ports and theyāre fairly simple, clk
and baud
are a given. But some of you might get confused about the sample
output, donāt fret it, Iāll touch on it later.
Weāll also need an extra register to count how many clock cycles it takes to toggle the baud signal, which is called the DIVIDER
(itās explained in the next code block). Hereās what our counter looks like -> reg [15:0] counter;
Parameters
parameter SYSTEM_CLOCK_FREQ = 100000000; // 100 MHz
parameter BAUD_RATE = 115200;
localparam int DIVIDER = SYSTEM_CLOCK_FREQ / BAUD_RATE;
My systemās clock frequency is 100 MHz because I toggle the clk
signal every 5 ns, so I declare it as such.
There are many common baud rates like 9600, 19200, 38400, ā¦ but I settled on 115200, for no particular reason other than wanting to get on with it. (Iām not sure how others make that choice, perhaps itās about cost or something similar).
The DIVIDER
is a parameter we use to divide the system clock frequency by the baud rate to determine how many clock cycles it takes for each baud
signal toggle. Thatās my simplified explanation. If you want the full details, feel free to check out this excerpt from wikipedia:
The symbol duration time, also known as the unit interval, can be directly measured as the time between transitions by looking at an eye diagram of the signal on an oscilloscope. The duration Ts can be calculated as:
where fs is the symbol rate. There is also a chance of miscommunication which leads to ambiguity. Example: Communication at the baud rate 1000 Bd means communication by means of sending 1000 symbols per second. The symbol duration time is 1/1000 second (that is, 1 millisecond).
This excerpt wasnāt necessary, but I feel that it could help the reader understand the concept more deeply in case of any future confusion. I know that it helped me clarify some misunderstandings.
Logic
always @(posedge clk) begin
if (counter >= DIVIDER - 1) begin
counter <= 0;
baud <= ~baud;
sample <= 0;
end else if (baud && counter >= DIVIDER / 2) begin
sample <= 1;
counter <= counter + 1;
end else begin
counter <= counter + 1;
end
end
Here weāve got 2 conditions (excluding the default condition).
What the first one does is fairly simple, once our counter reaches the DIVIDER
value i.e. 868 in our case. We reset every signal to 0 and toggle the baud signal.
The second condition is where I get to talk about the utility of a sample
signal, this is exclusively used in my receiver, but itās purpose is very interesting. The sample
signal is used to send a sample bit every time weāre in the middle of the symbol duration time i.e. in the middle of a baud
signal, which is a method used to ensure we reduce the risks of missing a bit, since by default, the ācatchingā logic on the reception side is done at the positive edge of the baud
signal we might try to catch it too early. So having a standardized way to ensure we only activate reception when thereās a sample bit is very useful! We increment the counter in here as well.
The default condition is fairly obvious, as long as we havenāt reached any of the previous conditions, we keep incrementing the counter.
Thatās pretty much it for my baud rate generator. If you have any questions or suggestions, feel free to reach out on X (formerly twitter). Or if you want to go take a look at the project and perhaps work on your own implementation of a UART, feel free to go fork it on GitHub.