module demo_wishbone_master(
  Reset_n,
  Clk,

  //--- UART interface

  // Pulsed when RxData_i is valid
  RxValid_i,
  RxData_i,

  // Asserted when ready for a new Tx byte
  TxReady_i,

  // Pulsed when TxData_o is valid
  TxValid_o,
  TxData_o,

  //--- Wishbone interface
  STB_ETH_O,
  STB_PDM_O,
  STB_PG_O,
  CYC_O,
  ADR_O,
  WE_O, 
  DAT_O,
  DAT_I,
  ACK_I
);

  input         Reset_n;
  input         Clk;

  //--- UART interface

  // Pulsed when RxData_i is valid
  input         RxValid_i;
  input [7:0]   RxData_i;

  // Asserted when ready for a new Tx byte
  input         TxReady_i;

  // Pulsed when TxData_o is valid
  output        TxValid_o;
  output [7:0]  TxData_o;

  output        STB_ETH_O;
  output        STB_PDM_O;
  output        STB_PG_O;
  output        CYC_O;
  output [14:0] ADR_O;
  output        WE_O;
  output [15:0] DAT_O;
  input [15:0]  DAT_I;
  input         ACK_I;

  //-------------------------------------------------------------------------
  // Local declarations
  //-------------------------------------------------------------------------

  reg          TxValid_o;
  reg [7:0]    TxData_o;
  reg          STB_ETH_O;
  reg          STB_PDM_O;
  reg          STB_PG_O;
  reg          CYC_O;
  reg [14:0]   ADR_O;
  reg          WE_O;
  reg [15:0]   DAT_O;

  //-------------------------------------------------------------------------
  // Instantiation of sub-modules
  //-------------------------------------------------------------------------

  //--- Transmit FSM --------------------------------------------------------

  parameter    TX_STATE_IDLE  = 0;
  parameter    TX_STATE_INIT  = 1;
  parameter    TX_STATE_OK    = 2;
  parameter    TX_STATE_ERROR = 3;
  parameter    TX_STATE_VALUE = 4;
  parameter    TX_STATE_LF    = 5;

  reg [2:0]    TxState;
  reg [3:0]    TxIndex;
  reg          TxLast;

  wire [15:0]  TxValue16;
  wire [3:0]   TxHexDigit;
  wire [7:0]   TxHexChar;
  reg          TxOK;
  reg          TxERROR;
  reg          TxValue;

  always @( negedge Reset_n or posedge Clk )
    if ( ~Reset_n )
      begin
        TxState   <= TX_STATE_INIT;
        TxIndex   <= 0;
        TxLast    <= 0;

        TxValid_o <= 0;
        TxData_o  <= 'b0;
      end
    else
      begin
        TxValid_o <= 0;

        // Don't do anything in cycle following TxValid_o being pulsed
        if ( ~TxValid_o )
          begin
            casez ( TxState )
              TX_STATE_INIT:
                casez ( TxIndex )
                  0: TxData_o <= "R";
                  1: TxData_o <= "E";
                  2: TxData_o <= "A";
                  3: TxData_o <= "D";
                  4: TxData_o <= "Y";
                  default: TxLast <= 1;
                endcase
              
              TX_STATE_OK:
                casez ( TxIndex )
                  0: TxData_o <= "O";
                  1: TxData_o <= "K";
                  default: TxLast <= 1;
                endcase

              TX_STATE_ERROR:
                casez ( TxIndex )
                  0: TxData_o <= "E";
                  1: TxData_o <= "R";
                  2: TxData_o <= "R";
                  3: TxData_o <= "O";
                  4: TxData_o <= "R";
                  default: TxLast <= 1;
                endcase

              TX_STATE_VALUE:
                casez ( TxIndex )
                  0,1,2,3: TxData_o <= TxHexChar;
                  default: TxLast <= 1;
                endcase

              TX_STATE_LF:
                ;

              default:
                begin
                  if ( TxOK )
                    TxState <= TX_STATE_OK;
                  else if ( TxERROR )
                    TxState <= TX_STATE_ERROR;
                  else if ( TxValue )
                    begin                         
                      TxState <= TX_STATE_VALUE;
                      TxIndex <= 0;
                    end
                end
            endcase

            if ( (TxState != TX_STATE_IDLE) & TxReady_i )
              begin
                TxValid_o <= 1;

                if ( TxLast )
                  begin
                    if ( TxState == TX_STATE_LF )
                      begin
                        TxData_o <= 10; // LF
                        TxState  <= TX_STATE_IDLE;
                        TxIndex  <= 0;
                        TxLast   <= 0;
                      end
                    else
                      begin
                        TxData_o <= 13; // CR
                        TxState  <= TX_STATE_LF;
                      end
                  end
                else
                  TxIndex <= TxIndex + 1;
              end
          end
      end

  assign TxHexDigit = (TxIndex==0) ? TxValue16[15:12] :
                      (TxIndex==1) ? TxValue16[11: 8] :
                      (TxIndex==2) ? TxValue16[ 7: 4] :
                                     TxValue16[ 3: 0];

  assign TxHexChar = (TxHexDigit <= 9) ? (TxHexDigit + "0") :
                                         (TxHexDigit + "A"-'hA);

  //--- Receive FSM ---------------------------------------------------------

  parameter RX_STATE_IDLE           = 0;
  parameter RX_STATE_VALUE16_FIRST  = 1;
  parameter RX_STATE_VALUE16        = 2;
  parameter RX_STATE_COMMENT        = 3;
  parameter RX_STATE_CMD            = 4;

  reg [2:0] RxState;

  wire IsWhiteSpace = ( RxData_i==" "  ) |
                      ( RxData_i=="\t" ) |
                      ( RxData_i==","  ) |
                      ( RxData_i==10   ) |
                      ( RxData_i==13   );
  wire IsHexDigit = (( RxData_i >= "0" ) & ( RxData_i <= "9" )) |
                    (( RxData_i >= "a" ) & ( RxData_i <= "f" )) |
                      (( RxData_i >= "A" ) & ( RxData_i <= "F" ));
  wire [3:0] RxHexValue =
               (( RxData_i >= "0" ) & ( RxData_i <= "9" )) ? RxData_i[3:0] :
               (( RxData_i >= "a" ) & ( RxData_i <= "f" )) ? (RxData_i-"a"+'hA) :
               (( RxData_i >= "A" ) & ( RxData_i <= "F" )) ? (RxData_i-"A"+'hA) : 0;

  reg [15:0] RxValue16;
  reg        RxWrite;
  reg        RxWrData;

  reg [15:0] RegAddr;
  reg [15:0] RegRdData;

  assign     TxValue16 = RegRdData;

  always @( negedge Reset_n or posedge Clk )
    if ( ~Reset_n )
      begin
        RxState   <= RX_STATE_IDLE;

        RxValue16 <= 16'h0;
        RxWrite   <= 0;
        RxWrData  <= 0;

        RegAddr   <= 'b0;
        RegRdData <= 'b0;

        STB_ETH_O <= 0;
        STB_PDM_O <= 0;
        STB_PG_O  <= 0;
        CYC_O     <= 0;
        ADR_O     <= 0;
        WE_O      <= 0;
        DAT_O     <= 0;

        TxOK      <= 0;
        TxERROR   <= 0;
        TxValue   <= 0;
      end
    else
      begin
        TxOK    <= 0;
        TxERROR <= 0;
        TxValue <= 0;

        if ( RxState == RX_STATE_CMD )
          begin
            STB_ETH_O <= ( RegAddr[15:12] == 4'h0 );
            STB_PG_O  <= ( RegAddr[15:12] == 4'h1 );
            STB_PDM_O <= ( RegAddr[15]    == 1'b1 );

            CYC_O <= 1;
            ADR_O <= RegAddr[14:0];
            WE_O  <= RxWrite;

            if ( ACK_I )
              begin
                // Register transaction is completing!
                CYC_O     <= 0;
                STB_ETH_O <= 0;
                STB_PDM_O <= 0;
                STB_PG_O  <= 0;

                // Latch data read in case of a read
                RegRdData <= DAT_I;

                if ( RxWrite )
                  // Transaction was a register write
                  TxOK <= 1;
                else
                  TxValue <= 1;

                RxState <= RX_STATE_IDLE;
              end
          end
        else if ( (TxState == TX_STATE_IDLE) & RxValid_i )
          begin
            // A byte has been received!

            casez ( RxState )
              RX_STATE_IDLE:
                if ( (RxData_i == "w") | (RxData_i == "W") )
                  begin
                    // Write Register Command: W rrrr dddd
                    RxState  <= RX_STATE_VALUE16_FIRST;
                    RxWrite  <= 1;
                    RxWrData <= 0;
                  end
                else if ( (RxData_i == "r") | (RxData_i == "R") )
                  begin
                    // Read Register Command: R rrrr
                    RxState <= RX_STATE_VALUE16_FIRST;
                    RxWrite <= 0;
                  end
                else if ( RxData_i == "/" )
                  begin
                    // Comment!
                    RxState <= RX_STATE_COMMENT;
                  end
                else if ( ~IsWhiteSpace )
                  // Unknown command!
                  TxERROR <= 1;

              RX_STATE_COMMENT:
                if ( (RxData_i == 13) | (RxData_i == 10) )
                  // CR or LF - end of comment
                  RxState <= RX_STATE_IDLE;

              RX_STATE_VALUE16_FIRST:
                if ( IsHexDigit )
                  begin
                    RxValue16 <= { 12'b0, RxHexValue };
                    RxState <= RX_STATE_VALUE16;
                  end
                else if ( ~IsWhiteSpace )
                  begin
                    // Unexpected character!
                    TxERROR <= 1;
                    RxState <= RX_STATE_IDLE;
                  end

              RX_STATE_VALUE16:
                if ( IsHexDigit )
                  RxValue16 <= { RxValue16[11:0], RxHexValue };
                else if ( IsWhiteSpace )
                  begin
                    // Done collecting 16-bit value
                    if ( RxWrite )
                      begin
                        // This is a register write
                        if ( RxWrData )
                          begin
                            // Second time around - just received write data
                            DAT_O   <= RxValue16;
                            RxState <= RX_STATE_CMD;
                          end
                        else
                          begin
                            // Just received register address - expecting second argument
                            RegAddr <= RxValue16;
                            RxState <= RX_STATE_VALUE16_FIRST;
                            RxWrData <= 1; // Now receive the write data
                          end
                      end
                    else
                      begin
                        // This is a register read
                        RegAddr <= RxValue16;
                        RxState <= RX_STATE_CMD;
                      end
                  end
                else
                  begin
                    // Unexpected character!
                    TxERROR <= 1;
                    RxState <= RX_STATE_IDLE;
                  end

              default:
                TxERROR <= 1;
            endcase
          end
      end
  
endmodule