UART Terminal Communication: Transmit Side

Introduction

Now that you’ve completed a UART transmitter, in this lab you will design a UART receiver, implement and test it alongside your transmitter, similar to what was done in the serial_interface and SPI_WRITE assignments.

As a reminder, the UART specs for this lab are:

Assigned Tasks

Design uart_rx

Design a UART receiver module with these I/O ports:

        clk    // system clock
        rst_l  // active-low reset

        rx     // UART serial data input
    
        d_in   // received data (8 bits)
    
        ready   // "data is ready" handshake signal  
        ack     // "data acknowledged" handshake signal

State Machine

The UART RX module is very similar to the TX design, except the flow of information is reversed. Use the TX state machine as a starting point, and design a modified state machine to produce the receiver’s behavior. Note that this is very similar to what was done in previous assignments.

For handshaking on the application side, use a procedure similar to the S2P module from the serial_interface assignment. Include handshaking signals in your state machine.

Scan an image of your state transition diagram and add/commit it to the repository.

Program your state machine in the uart_rx module.

Clock Divider with Synchronization

The UART RX is very similar to the SPI RX, with one important difference: since the UART channel has no clock, you should initialize the UART clock divider so that it has a rising edge immediately when the transmission begins.

To support this timing synchronization requirement, modify the clock divider. Supposing your divider has a UART clock named div_clk with a counter named div_count, you should modify the logic as follows:

Example solution

One approach is to use a clock_divider submodule with a sync input. Whenever sync goes high, it forces the clock divider output to zero. When sync goes low again, the clock divider resets its phase with a rising edge a short delay after sync event. In the example below, the delay is set to 1000 cycles of the main system clock. The purpose of this delay is to let the rx signal settle to a stable value before reading its value.

module clock_divider
  #(
    parameter N=5208
    )
   (
    input      clk,
    input      rst_l,
    input      sync,
    output reg div_clk
    );
   
   integer     clk_count;
   
   initial begin
      div_clk   = 0;
      clk_count = 0;
   end

   always @(posedge clk, negedge rst_l) begin
      if (!rst_l) begin
        div_clk   <= 0;
        clk_count <= 0;       
      end
      else begin
        //---- sync behavior -----------------//
        if (sync) begin
           div_clk   <= 0;        // set div_clk low 
           clk_count <= N-1000;   // delay 1000 cycles before rising edge
        end
        //---- normal clock divider below ----//
        else if (clk_count == N) begin
           div_clk   <= ~div_clk;
           clk_count <= 0;          
        end
        else begin
           clk_count <= clk_count + 1;          
        end
        //-------------------------------------//
      end
   end
   
endmodule // clock_divider

Simulate uart_rx

Modify the testbench from uart_tx so that it sends a transmission. Declare the necessary wire and reg signals that correspond to the uart_rx ports. Remove or comment out unused signals from the tx module.

The section to focus on is this one:

      rx    = 1;         // <--- add this line to initialize rx in the idle mode
      ack   = 0;         // <--- add this line to initialize handshake
      
      #1000 send = 1;    // <--- remove this line

      while (1) begin
      
         d_out = $random(); // <--- add this line to set data to send
                            
         // Wait for a signal:               // <--- remove
         while (tx) begin                    //      all
            // Delay 10us between checks     //      of
           #10000;                           //      these
         end                                 //      lines

         $write("Sending %x\n",d_out);        // <-- add this
         $fwrite(fid,"Sending %x\n",d_out);   // <-- and this


         $write("Start bit. TX=");       // <--- change "TX" to "RX"
         $fwrite(fid,"Start bit. TX="); 

         rx=0;   // <--- add this line to send START BIT

         // SEND eight bits:
         for (i=0; i<8; i=i+1) begin
            #104170;
            d_in[i] = tx;          // <-- Change to rx = d_out[i]
            $write("%b",tx);       // <-- tx to rx
            $fwrite(fid,"%b",tx);  // <-- ditto
         end

         // Wait for stop bit:
         #104170;
         
         if (tx) begin               // <-- remove the `if` line
            $write(". Stop bit. ");
            $fwrite(fid,". Stop bit. ");
         end                                   // <-- remove
         else begin                            // <-- remove
            $write(". BAD STOP BIT. ");        // <-- remove
            $fwrite(fid,". BAD STOP BIT. ");   // <-- remove
         end                                   // <-- remove

         // Add these lines:
         while (!ready) begin
            #104170;  // wait for 'ready' signal
         end
         ack = 1;
       
         // Keep these lines:
         $write("Got %xh('%c')\n",d_in,d_in);
         $fwrite(fid,"Got %xh ('%c')\n",d_in,d_in);

         // Add these lines to test handshake:
         while (ready) begin
            #104170;  // wait for 'ready' signal to drop
         end
         ack = 0;    // clear ack
       
         #104170;  // wait one more UART clock cycle before sending again
     
         tx_count = tx_count + 1;       
      end

Complete your testbench, run the simulation and verify that your module receives the same data that was transmitted.

Design a top module

Create a top module based on the SPI_WRITE assignment. Instead of the SPI modules, place one instance of uart_tx and one instance of uart_rx, and connect the tx port from one to the rx port of the other. The uart_tx module will send data from the switches, and uart_rx will write data to the LEDs.

Use debouncers for handshaking

Use buttons for the start and ack signals. Use modified debouncers to implement the handshaking protocols.

Testbench simulation

Create a new testbench based on the SPI_WRITE assignment to test your top module. Don’t delete the other testbench that you made; instead, name this one top_testbench and edit the Makefile by adding these lines:

top: compile elaborate_top simulate

elaborate_top: compile 
      xelab -debug typical -s sim top_testbench

Important note: there should be a TAB prior to the xelab command, not spaces.

Then run your simulation using the command make top.

Implement and Test the Design

Create build.tcl and XDC files for your top module, implement it and program onto the Basys3 board.

After the design is debugged and verified to work properly, record a short video showing your design work and post the video to Canvas.

Turn in your work using git:

git add src/*.v *.v *.rpt *.txt *.tcl *.bit *.xdc
git commit . -m "Complete"
git push origin master