-
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 sim2
The simulation in gtkwave is shown:
The character received in serial by the rx line is transmit through tx and displayed in the leds
For synthesizing the example, execute the following command:
$ make sint2
The resources used by the example are:
Resource | used |
---|---|
PIOs | 10 / 96 |
PLBs | 46 / 160 |
BRAMs | 0 / 16 |
For uploading into the ICEstick, execute:
$ make prog2
For testing, launch the gtkterm application (or any other serial terminal program). All the keys typed will be sent to the icestick and received back in the terminal
Juan González Gómez (Obijuan)
Licensed under a GPL v3 (code) and a Creative Commons Attribution-ShareAlike 4.0 International License (figures and images)
- Clifford Wolf, Mathias Lasser. Project IceStorm Thanks!
- Although this receiver has been written from the scratch, it has been inspired by the one developed in the swapforth proyect by James Bowman. Thanks!
- BQ sponsored this project from 2015-07-01 to 2016-04-14. Thanks!