// UART driver modules // // HHP 7 January 2020 // HHP 29 November 2022 // // Contains Modules: // uart_rx // uart_tx // uart_tb -- Testbench // // IDLE and STOP bits are HIGH (1) // START bit is LOW (0) // Sends 1 STOP bit, no parity // Sends 8 data bits; sends LSB bit first // // Assuming we use the 50 MHz clock // 20 nSec x 50,000,000 = 1 Sec // So the clock period = 20 nSec // For 9600 baud... // 50,000,000 / 9600 = 5208.33 Clocks Per Bit. // For 115,200 baud... // 50,000,000 / 115,200 = 434 Clocks Per Bit. // // WARNING: The two clock counters are only 16 bits, so watch for // overflow if the "Clocks per bit" value gets large, i.e., // with a really fast clock and a really low baud rate. // // parameter c_CLOCK_PERIOD_NS = 20; // parameter c_CLKS_PER_BIT = 434; //------------------------------------------------------------- // // Originally downloaded from http://www.nandland.com // // Modified by HHP // // This module is the UART Receiver. This receiver is able to // receive 8 bits of serial data, one start bit, one stop bit, // and no parity bit (i.e., 8-N-1). When receive is complete, // o_data_avail will be driven high for one clock cycle. // // Set Parameter CLKS_PER_BIT as follows: // CLKS_PER_BIT = (Frequency of clock)/(Frequency of UART) // Example: 10 MHz Clock, 115200 baud UART // 10000000/115200 = 87 module uart_rx #(parameter CLKS_PER_BIT) ( input clock, input i_rx, output o_data_avail, output [7:0] o_data_byte ); localparam IDLE_STATE = 2'b00; localparam START_STATE = 2'b01; localparam GET_BIT_STATE = 2'b10; localparam STOP_STATE = 2'b11; reg rx_buffer = 1'b1; reg rx = 1'b1; reg [1:0] state = 0; reg [15:0] counter = 0; reg [2:0] bit_index = 0; // where to place the next bit (0...7) reg data_avail = 0; reg [7:0] data_byte = 0; assign o_data_avail = data_avail; assign o_data_byte = data_byte; // Double-buffer the incoming RX line. This allows it to be // used in the UART RX Clock Domain and removes problems caused // by metastability. always @(posedge clock) begin rx_buffer <= i_rx; rx <= rx_buffer; end // The state machine. always @(posedge clock) begin case (state) IDLE_STATE : begin data_avail <= 0; counter <= 0; bit_index <= 0; if (rx == 0) // Start bit detected state <= START_STATE; else state <= IDLE_STATE; end // Wait until the middle of the start bit START_STATE : begin if (counter == (CLKS_PER_BIT-1)/2) begin if (rx == 0) // If still low at the middle of the start bit... begin counter <= 0; state <= GET_BIT_STATE; end else begin state <= IDLE_STATE; end end else begin counter <= counter + 16'b1; state <= START_STATE; end end // Wait CLKS_PER_BIT-1 clock cycles to sample RX for next bit GET_BIT_STATE : begin if (counter < CLKS_PER_BIT-1) begin counter <= counter + 16'd1; state <= GET_BIT_STATE; end else begin counter <= 0; data_byte[bit_index] <= rx; // Check if we have received all bits if (bit_index < 7) begin bit_index <= bit_index + 3'd1; state <= GET_BIT_STATE; end else begin state <= STOP_STATE; end end end // Wait until this middle of the Stop bit. STOP_STATE : begin if (counter < CLKS_PER_BIT-1) begin counter <= counter + 16'b1; state <= STOP_STATE; end else begin data_avail <= 1; // Signal that we have a complete byte. state <= IDLE_STATE; end end default: state <= IDLE_STATE; endcase end endmodule //------------------------------------------------------------- // // Originally downloaded from http://www.nandland.com // // Modified by HHP // // This module is the UART Transmitter. This transmitter is able // to transmit 8 bits of serial data, one start bit, one stop bit, // and no parity bit. When transmit is complete o_done will be // driven high for one clock cycle. // // Set Parameter CLKS_PER_BIT as follows: // CLKS_PER_BIT = (Frequency of clock)/(Frequency of UART) // Example: 10 MHz Clock, 115200 baud UART // 10000000/115200 = 87 module uart_tx #(parameter CLKS_PER_BIT) ( input clock, input i_data_avail, // If HIGH, start transmission input [7:0] i_data_byte, // Latched when i_data_avail is HIGH output reg o_active, // HIGH while busy transmitting output reg o_tx, // Connect this to the Tx line output reg o_done // HIGH for one clock when complete ); localparam IDLE_STATE = 2'b00; localparam START_STATE = 2'b01; localparam SEND_BIT_STATE = 2'b10; localparam STOP_STATE = 2'b11; reg [1:0] state = 0; reg [15:0] counter = 0; reg [2:0] bit_index = 0; reg [7:0] data_byte = 0; always @(posedge clock) begin case (state) IDLE_STATE : begin o_tx <= 1; o_done <= 0; counter <= 0; bit_index <= 0; if (i_data_avail == 1) begin o_active <= 1; data_byte <= i_data_byte; state <= START_STATE; end else begin state <= IDLE_STATE; o_active <= 0; end end // Send Start Bit START_STATE : begin o_tx <= 0; // Wait CLKS_PER_BIT-1 clock cycles for start bit to finish if (counter < CLKS_PER_BIT-1) begin counter <= counter + 16'd1; state <= START_STATE; end else begin counter <= 0; state <= SEND_BIT_STATE; end end // Wait CLKS_PER_BIT-1 clock cycles for each data bit to finish SEND_BIT_STATE : begin o_tx <= data_byte[bit_index]; if (counter < CLKS_PER_BIT-1) begin counter <= counter + 16'b1; state <= SEND_BIT_STATE; end else begin counter <= 0; // Check if we have sent out all bits if (bit_index < 7) begin bit_index <= bit_index + 3'd1; state <= SEND_BIT_STATE; end else begin state <= STOP_STATE; end end end // Send Stop bit STOP_STATE : begin o_tx <= 1; // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish if (counter < CLKS_PER_BIT-1) begin counter <= counter + 16'b1; state <= STOP_STATE; end else begin o_done <= 1; // Raise o_done for final clock cycle o_active <= 0; state <= IDLE_STATE; end end default : state <= IDLE_STATE; endcase end endmodule //------------------------------------------------------------- // // Originally downloaded from http://www.nandland.com // // Modified by HHP // // This testbench will exercise both the UART Tx and Rx. // It sends out byte 0xAB over the transmitter // It then exercises the receive by receiving byte 0x3F `timescale 1ns/10ps /******************** THE TESTING CODE IN SecondLevelMain.sv IS BETTER ******************** module uart_tb (); // Testbench uses a 10 MHz clock // Want to interface to 115200 baud UART // 10000000 / 115200 = 87 Clocks Per Bit. parameter c_CLOCK_PERIOD_NS = 100; parameter c_CLKS_PER_BIT = 87; parameter = 8600; reg r_Clock = 0; reg r_Tx_DV = 0; wire w_Tx_Done; reg [7:0] r_Tx_Byte = 0; reg r_Rx_Serial = 1; wire [7:0] w_Rx_Byte; // Takes in input byte and serializes it task UART_WRITE_BYTE; input [7:0] i_Data; integer ii; begin // Send Start Bit r_Rx_Serial <= 1'b0; #(c_BIT_PERIOD); #1000; // Send Data Byte for (ii=0; ii<8; ii=ii+1) begin r_Rx_Serial <= i_Data[ii]; #(c_BIT_PERIOD); end // Send Stop Bit r_Rx_Serial <= 1'b1; #(c_BIT_PERIOD); end endtask // UART_WRITE_BYTE uart_rx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_RX_INST (.clock(r_Clock), .i_rx(r_Rx_Serial), .o_data_avail(), .o_data_byte(w_Rx_Byte) ); uart_tx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_TX_INST (.clock(r_Clock), .i_data_avail(r_Tx_DV), .i_data_byte(r_Tx_Byte), .o_active_output(), .o_tx(), .o_done(w_Tx_Done) ); always #(c_CLOCK_PERIOD_NS/2) r_Clock <= !r_Clock; // Main Testing: initial begin // Tell UART to send a command (exercise Tx) @(posedge r_Clock); @(posedge r_Clock); r_Tx_DV <= 1'b1; r_Tx_Byte <= 8'hAB; @(posedge r_Clock); r_Tx_DV <= 1'b0; @(posedge w_Tx_Done); // Send a command to the UART (exercise Rx) @(posedge r_Clock); UART_WRITE_BYTE(8'h3F); @(posedge r_Clock); // Check that the correct command was received if (w_Rx_Byte == 8'h3F) $display("Test Passed - Correct Byte Received"); else $display("Test Failed - Incorrect Byte Received"); end endmodule ********************************************************************************/