-
Notifications
You must be signed in to change notification settings - Fork 35
Asynchronous serial receiver unit
Asynchronous serial receiver unit for the Icestick board, synthetized with Opensource Icestorm tools
- Baudrates: 300, 600, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
- Clock frequency: 12Mhz
- Start bits: 1
- Data bits: 8
- Parity: None
- Stop bits: 1
- Description language: Verilog
- Toolchain: Opensource: Yosys, Arachne-pnr, Icestorm project
Serial packages consist of three parts: the start bit, the 8-bit data and the stop bit
Example of the serial package for the K character (ASCII 0x4B: Binary: 01001011)
The serial receiver is encapsulated in the uart-rx entity
The receiver unit has 3 inputs and 2 outputs:
-
Inputs:
- clk: System clock (12MHz in the ICEstick board)
- rstn: Active low. When rstn is 0, the serial unit is reset (synchronous reset)
- rx: Serial input. The serial packages are received from this input
-
Outputs:
- data: 8-bit data to transmit
- rcv: Character received. When a new character is received, a pulse of duration 1 clock cycle is emitted, so that it can be used for capturing the data.
The step for receiving a character are the following:
- Wait until the rcv signal is 1. It will remains 1 only during 1 system clock cycle
- Capture the character (8 bits) from the data output
- Repeat the process
The implementation of the receiver is shown in the given block diagram:
It consist of the following parts:
- Data register (8 bits): For storing the received character. It is captured when the load signal is active (1)
- Shift register (10 bits): For storing the serial bits received. Every bit is captured when the clk_baud signal is 1. This signal determines the exact time for sampling the incoming bits
- Baud generator: It generates a pulse of 1 cycle periodically, according to the baud rate configured
- Bit counter: It counts the bits that have already been received. It is reset to 0 when the clear signal is set to 1
- Controller: The finite state machine that generates the clear, load and bauden control signals for controlling the transmitter
- D flip-flops: For registering the rx input signal and get it synchronized
The receiver controller is a finite state machine with four states:
- IDLE: The unit is waiting for the start bit of the character. When it is received, it is changed to the RCV state. The bit counter is set to 0 (clear signal is 1)
- RCV: The baud generator is enabled and the bits are stored into the shift register. When a serial package (10 bits) is received, the state is changed to LOAD
- LOAD: Store the received data. The load control signal is activated (1)
- DAV: (Data available). A new data is available for reading. The signal rcv is set to 1 during one clock cycle
This is a generic code for using the UART-rx in your designs in verilog. Of course, some details can change depending on the particular implementation (as for example the definitions of the wires)
//-- Baudrate definitions
`include "baudgen.vh"
//-- Definitions of wires to connecto to the unit
wire clk;
wire rstn;
wire rcv;
wire [7:0] data;
wire rx;
//-- Receiver unit instantiation
//-- Values for BAUDRATE
//-- `B115200, `B57600, `B38400, `B19200, `B9600, `B4800, `B2400,
//-- `B1200, `B600, `B300
uart_rx #(.BAUDRATE(`B115200))
RX0 (.clk(clk),
.rstn(rstn),
.rx(rx),
.rcv(rcv),
.data(data)
);
Two examples in verilog on how to use the UART-rx unit are shown
The four less significant bits of the received data are shown in the icestick red leds. The UART-rx unit is instantiated and configured at 115200 baudrate
`default_nettype none
`include "baudgen.vh"
//-- Top entity
module rxleds #(
parameter BAUDRATE = `B115200
)(
input wire clk, //-- System clock
input wire rx, //-- Serial input
output reg [3:0] leds //-- Red leds
);
//-- Received character signal
wire rcv;
//-- Received data
wire [7:0] data;
//-- Reset signal
reg rstn = 0;
//-- Initialization
always @(posedge clk)
rstn <= 1;
//-- Receiver unit instantation
uart_rx #(BAUDRATE)
RX0 (.clk(clk), //-- System clock
.rstn(rstn), //-- Reset (Active low)
.rx(rx), //-- Serial input
.rcv(rcv), //-- Character received notification (1)
.data(data) //-- Character received
);
//-- Register the character received and show its 4 less significant leds
//-- in the icestick leds
always @(posedge clk)
if (!rstn)
leds <= 0;
//-- When there is data available, capture it!
else if (rcv == 1'b1)
leds <= data[3:0];
endmodule
for simulating, execute the command:
$ make sim
The simulation in gtkwave is shown:
The testbench sends two character: 0x55 and 0x4B, that are received by the unit. They can be seen in the data register. Also, the 4 less significant bits are shown in the leds. It can also be seen that the clk_baud pulses are generated in the middle of the received bits
For synthesizing the example, execute the following command:
$ make sint
The resources used by the example are:
Resource | used |
---|---|
PIOs | 6 / 96 |
PLBs | 21 / 160 |
BRAMs | 0 / 16 |
for uploading into the ICEstick execute:
$ make prog
Launch the gtkterm application (or any other serial terminal program). Configure the baudrate to 115200. Type any character. You will see their 4 less significant bits in the red leds
This is the classic example of echo: Al the characters received are sent back and can be seen on the terminal
//----------------------------------------------------------------------------
//-- echo.v: Uart-rx example 2
//-- All the received characters are echoed
//----------------------------------------------------------------------------
//-- (C) BQ. December 2015. Written by Juan Gonzalez (Obijuan)
//-- GPL license
//----------------------------------------------------------------------------
`default_nettype none
`include "baudgen.vh"
//-- Top design
module echo #(
parameter BAUDRATE = `B115200
)(
input wire clk, //-- System clock
input wire rx, //-- Serial input
output wire tx, //-- Serial output
output reg [3:0] leds //-- Red leds
);
//-- Received character signal
wire rcv;
//-- Received data
wire [7:0] data;
//-- Reset signal
reg rstn = 0;
//-- Transmitter ready signal
wire ready;
//-- Initialization
always @(posedge clk)
rstn <= 1;
//-- Turn on all the red leds
//assign leds = 4'hF;
//-- Show the 4 less significant bits in the leds
always @(posedge clk)
leds = data[3:0];
//-- Receiver unit instantation
uart_rx #(.BAUDRATE(BAUDRATE))
RX0 (.clk(clk),
.rstn(rstn),
.rx(rx),
.rcv(rcv),
.data(data)
);
//-- Transmitter unit instantation
uart_tx #(.BAUDRATE(BAUDRATE))
TX0 ( .clk(clk),
.rstn(rstn),
.start(rcv),
.data(data),
.tx(tx),
.ready(ready)
);
endmodule
For simulating, execute the command:
$ make sim
The simulation in gtkwave is shown: