Skip to content

Asynchronous serial receiver unit

Juan Gonzalez-Gomez edited this page Dec 18, 2015 · 30 revisions

Access to the repo

Introduction

Asynchronous serial receiver unit for the Icestick board, synthetized with Opensource Icestorm tools

Features

  • 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

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)

UART-RX details

The serial receiver is encapsulated in the uart-rx entity

Ports

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.

Chronogram

The step for receiving a character are the following:

  1. Wait until the rcv signal is 1. It will remains 1 only during 1 system clock cycle
  2. Capture the character (8 bits) from the data output
  3. Repeat the process

Block diagram

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

Controller

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

Using UART-rx in Verilog

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)
      );

Examples

Two examples in verilog on how to use the UART-rx unit are shown

rxleds.v: Showing the received chars on the leds

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

echo.v: Echoing the received characters

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:

Clone this wiki locally