`resetall
`timescale 1ns/10ps


module UART_verilog(
    clock,
    reset_N,
    address,
    writeData,
    write,
    readData,
    read,
    serialIn,
    serialOut
);


// Internal Declarations

input        clock;
input        reset_N;
input        address;
input  [7:0] writeData;
input        write;
output [7:0] readData;
input        read;
input        serialIn;
output       serialOut;


wire       clock;
wire       reset_N;
wire       address;
wire [7:0] writeData;
wire       write;
wire [7:0] readData;
wire       read;
wire       serialIn;
wire       serialOut;

// hds interface_end




    parameter ONCE  = 1,
              TWICE = 2;

    parameter IDLE  = 0,
              START = 1,
              SEND  = 2,
              STOP  = 3;

    parameter // IDLE    = 0,
              // START   = 1,
              RECEIVE = 2,
              // STOP    = 3,
              FULL    = 4,
              ERROR   = 5;

    parameter c_TX      = 0,
              c_RX      = 0,
              c_STATUS  = 1;

    parameter RxReady = 0,
              RxError = 1,
              TxReady = 2;

    reg       s_serialOut;
    reg [7:0] s_readData;




    reg [2:0]   RxState;
    reg [1:0]   TxState;
    reg [2:1]   serialInRetimed;

    reg [7:0]   Tx,
                Rx;

    reg [3:0]   RxSampleCount,
                RxBitCount,
                TxSampleCount,
                TxBitCount;

    reg       v_serialOut;
    reg [7:0] v_readData;

    //+++++++++++++++++++++++++++
    //                          +
    //   Main clocked logic.    +
    //                          +
    //+++++++++++++++++++++++++++

always @(posedge clock or negedge reset_N)
    if( !reset_N )
        begin
        RxState          = IDLE;
        TxState          = IDLE;

        Tx               = 8'b0;
        Rx               = 8'b0;

        serialInRetimed  = {1'b1,1'b1};
      s_serialOut       <= 1'b1;
        RxSampleCount    = 0;
        RxBitCount       = 0;
        TxSampleCount    = 0;
        TxBitCount       = 0;
        end

    else

        begin

        //++++++++++++++++++++++++++++++++++++++++++++++++
        //                                               +
        //   Assign all temporary registers in order to  +
        //   avoid generating unwanted flip-flops.       +
        //                                               +
        //++++++++++++++++++++++++++++++++++++++++++++++++

        v_serialOut = s_serialOut;
        v_readData  = 8'b0;




        //+++++++++++++++++++++++++++++
        //                            +
        //   Read/Write Registers.    +
        //                            +
        //+++++++++++++++++++++++++++++

        if( write == 1'b1 )
            case( address )

                c_TX:  begin
                       Tx = writeData;
                       TxState = START;
                       end

                default:;

            endcase




        if( read == 1'b1 )
            case( address )

                c_RX:        begin
                           v_readData = Rx;
                             RxState  = IDLE;
                             end

                c_STATUS:    begin

                             if( RxState==FULL )
                               v_readData[RxReady] = 1'b1;

                             if( RxState==ERROR )
                                 begin
                               v_readData[RxError] = 1'b1;
                                 RxState = IDLE;
                                 end

                             if( TxState==IDLE )
                               v_readData[TxReady] = 1'b1;

                             end

                default:;
            endcase




        //+++++++++++++++++++++++++++++++
        //                              +
        //   Transmit State Machine.    +
        //                              +
        //+++++++++++++++++++++++++++++++

        case( TxState )

            IDLE:     ;

            START:    begin
                    v_serialOut     = 1'b0;             // Start Bit.
                      TxSampleCount = 0;
                      TxBitCount    = 0;
                      TxState       = SEND;
                      end

            SEND:     begin
                      TxSampleCount = TxSampleCount+1;  // Eight Data Bits.
                      if( TxSampleCount == 8 )
                          begin
                          TxSampleCount = 0;
                        v_serialOut = Tx[TxBitCount];
                          TxBitCount = TxBitCount+1;
                          if( TxBitCount==8 )
                              TxState = STOP;
                          end
                      end

            STOP:     begin
                      TxSampleCount = TxSampleCount+1;
                      if( TxSampleCount == 8 )
                          begin
                        v_serialOut = 1'b1;              // Stop Bit.
                          TxState = IDLE;
                          end
                      end
        endcase




        //++++++++++++++++++++++++++++++
        //                             +
        //   Receive State Machine.    +
        //                             +
        //++++++++++++++++++++++++++++++

        case( RxState )

            IDLE:      if( serialInRetimed[TWICE] == 1'b0 )      // Falling Edge of Start Bit.
                           begin
                           RxSampleCount = 0;
                           RxBitCount    = 0;
                           RxState = RECEIVE;
                           end

            START:     begin
                       RxSampleCount = RxSampleCount+1;
                       if( RxSampleCount == 4 )                  // Centre of Start Bit
                           RxState = RECEIVE;
                       end

            RECEIVE:   begin
                       RxSampleCount = RxSampleCount+1;          // Eight Data Bits.
                       if( RxSampleCount == 8 )
                           begin
                           RxSampleCount = 0;
                           Rx[RxBitCount] = serialInRetimed[TWICE];
                           RxBitCount     = RxBitCount+1;
                           if( RxBitCount==8 )
                               RxState = STOP;
                           end
                       end

            STOP:      begin
                       RxSampleCount = RxSampleCount+1;
                       if( RxSampleCount == 8 )
                           if( serialInRetimed[TWICE] == 1'b1 )  // Check Stop Bit
                               RxState = FULL;
                           else
                               RxState = ERROR;
                       end

            FULL:      ;

            ERROR:     ;

        endcase




        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //                                                          +
        //   Re-time any asynchronous signals before use in order   +
        //   to avoid race hazards or problems of metastability     +
        //                                                          +
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        serialInRetimed[TWICE] = serialInRetimed[ONCE];
        serialInRetimed[ONCE]  = serialIn;




        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //                                                                  +
        //   Assign the temporary registers to their `real' counterparts.   +
        //                                                                  +
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        s_serialOut <= v_serialOut;
        s_readData  <= v_readData;

        end




    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //                                                                 +
    //   Assign the outputs from their associated internal signals.    +
    //                                                                 +
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

assign serialOut = s_serialOut;
assign readData  = s_readData;





endmodule