/*
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                                                                                                 ┃
┃          Program Name: ATx.v                                                                    ┃
┃                Author: Michael Krämer                                                           ┃
┃                 Email: Michael@dk8pp.de                                                         ┃
┃                  Date: 22.11.2025                                                               ┃
┃                  Tool: Gowin FPGA Designer V1.9.11.01 Education                                 ┃
┃                Device: GW5A-LV25MG121 Typ A                                                     ┃
┃               Version: 0.1                                                                      ┃
┃        Program status: still testing                                                            ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  Purpose: Asynchronous transmitter (like a UART, but TX only)                                   ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  © Copyright 2025-2026 Michael Krämer, DK8PP                                                    ┃
┃                                                                                                 ┃
┃  This program is free software: you can redistribute it and/or modify it under the terms of     ┃
┃  the GNU Lesser General Public License as published by the Free Software Foundation, either     ┃
┃  version 3 of the License, or (at your option) any later version.                               ┃
┃                                                                                                 ┃
┃  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;      ┃
┃  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.      ┃
┃  See the GNU Lesser General Public License for more details.                                    ┃
┃                                                                                                 ┃
┃  You should have received a copy of the GNU Lesser General Public License along with this       ┃
┃  program.   If not, see <http://www.gnu.org/licenses/>.                                         ┃
┃                                                                                                 ┃
┃  ============================================================================================   ┃
┃                                                                                                 ┃
┃  Dieses Programm ist Freie Software: Sie können es unter den Bedingungen der GNU Lesser         ┃
┃  General Public License, wie von der Free Software Foundation, Version 3 der Lizenz oder        ┃
┃  (nach Ihrer Wahl) jeder neueren veröffentlichten Version, weiter verteilen und/oder            ┃
┃  modifizieren.                                                                                  ┃
┃                                                                                                 ┃
┃  Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch OHNE   ┃
┃  JEDE GEWÄHR,; sogar ohne die implizite Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN        ┃
┃  BESTIMMTEN ZWECK. Siehe die GNU Lesser General Public License für weitere Einzelheiten.        ┃
┃                                                                                                 ┃
┃  Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit diesem Programm      ┃
┃  erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.                             ┃
┃                                                                                                 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

This module is an asynchronous transmitter according to the UART standard, but it is neither
 "universal" nor does it contain a receiver! It includes a baudrate generator and it is buffered.
 Transmit data is written to this buffer, which is copied to the shift register as soon as the
 previous transmission is complete. A transmit buffer empty interrupt is generated as soon as the
 shift register has shifted out the start bit. This should give the CPU sufficient time to
 complete any previous transfer.
 Stop bit count is fixed to one and parity is not supported.

 Design philosophy: Quick & Dirty!

*/

module ATx
    #(parameter BRG_BITSIZE = 10,       /* width of baudrate generator */
      BRG_RELOAD = 347,                 /* default BRG reload value, 347 for 115200 Bd @ 40 MHz */
      ADSIZE = 4                        /* default size of address decoder */
    )
    (input  wire cpuclk,                /* cpu clock */
     input  wire reset,                 /* reset input, active high */
     input  wire [ADSIZE-1:0] addr_in,  /* address input */
     input  wire [31:0] data_in,        /* data input */
     output wire [31:0] data_out,       /* data output */
     input  wire mod_sel,               /* module select */
     input  wire cpu_write,             /* CPU write cycle */
     output wire txd_out,               /* transmit data serial output */

/* optional test signals */
     output wire tx_active_out,         /* transmission is active */
     output wire txbuffer_valid_out,    /* valid data in transmission buffer */
     output wire wr_intack_out,         /* interrupt acknowledge */

     output wire intrq                  /* interrupt request */
    ) ;

    reg [BRG_BITSIZE-1:0] brg_reload ;  /* baudrate generator reload value */
    reg [BRG_BITSIZE-1:0] brg ;         /* baudrate generator */
    reg [9:0] output_buffer ;           /* shift register for tx-data, start- and stop-bits */
    reg tx_active ;
    reg [3:0] bitcnt ;
    reg intrq_reg ;                     /* interrupt request flag, transmit buffer is empty */
    reg [7:0] txbuffer ;                /* buffered transmit data */
    reg txbuffer_valid ;                /* valid data in transmit data */

assign txd_out = tx_active ? output_buffer[9] : 1'b1 ;
assign intrq = intrq_reg ;
assign tx_active_out = tx_active ;
assign txbuffer_valid_out = txbuffer_valid ;
assign wr_intack_out = wr_intack ;

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Address mapping                                                                           │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯

Asynchronous Transmitter ATx register address offsets:

AT_TXB_OFFSET: ATx transmit buffer (W)
    This is the transmit data for the asychronous transmitter

AT_BRGRLD_OFFSET: BRG Reload Register (R/W)

AT_TXBE_ACK_OFFSET: Interrupt acknowledge (W)
    Write bit 0 = 1 to acknowledge a pending ATx interrupt

*/

localparam AT_TXB_OFFSET =      32'h00 ;    /* Asynchronous Transmitter ATx offset of transmit buffer */
localparam AT_BRGRLD_OFFSET =   32'h04 ;    /* Asynchronous Transmitter BRG Reload register offset */
localparam AT_TXBE_ACK_OFFSET = 32'h08 ;    /* Asynchronous Transmitter ATx offset of interrupt acknowledge register */

wire [ADSIZE-1:0] addr_txb      = AT_TXB_OFFSET ;
wire [ADSIZE-1:0] addr_brgrld   = AT_BRGRLD_OFFSET ;
wire [ADSIZE-1:0] addr_intack   = AT_TXBE_ACK_OFFSET ;

wire wr_txd =    cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_txb) ;    /* write reload register */
wire wr_brgrld = cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_brgrld) ; /* write config register */
wire wr_intack = cpu_write & mod_sel & data_in[0] & (addr_in[ADSIZE-1:0] == addr_intack) ; /* acknowledge interrupt */

wire rd_rld = ~cpu_write & mod_sel & (addr_in[ADSIZE-1:0] == addr_brgrld) ;    /* read BRG reload register */

/* read data */
assign data_out = rd_rld ? brg_reload : 32'h00 ;

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       cpuclk clock domain                                                                       │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
always @(posedge cpuclk)
begin
   if (reset)
   begin
      brg_reload <= BRG_RELOAD ;
      brg <= BRG_RELOAD ;
      tx_active <= 0 ;
      bitcnt <= 0 ;
      intrq_reg <= 0 ;
      txbuffer_valid <= 0 ;
      txbuffer <= 0 ;
   end
   else
   begin
      if (wr_intack) intrq_reg <= 0 ; /* remove interrupt request when acknowledged by interrupt handler */

      if (wr_txd & ~txbuffer_valid)
      begin
         txbuffer <= {data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6], data_in[7]} ;
         txbuffer_valid <= 1 ;
      end

      if (wr_brgrld)
      begin
         brg_reload <= data_in[BRG_BITSIZE-1:0] ;
      end

      if (txbuffer_valid & ~tx_active)
      begin /* load tx data into output buffer, ignore while transmitting */
         output_buffer <= {1'b0, txbuffer, 1'b1} ;
         txbuffer_valid <= 0 ;
         tx_active <= 1 ;
         bitcnt <= 9 ;
         brg <= brg_reload ; /* reload BRG, otherwise start bit length is not guaranteed */
      end
      else
      begin
         if (brg != 0)
         begin /* decrement BRG */
            brg <= brg - {{BRG_BITSIZE-1{1'b0}},1'b1} ;
         end
         else
         begin /* next bit if any */
            brg <= brg_reload ; /* reload BRG */
            if (tx_active)
            begin
               if ((bitcnt == 4'd9) & (~txbuffer_valid))
               begin
                  intrq_reg <= 1 ;
               end

               if (bitcnt > 0)
               begin
                  bitcnt <= bitcnt - 4'b1 ;
                  output_buffer <= {output_buffer[8:0], 1'b0} ;
               end
               else
               begin
                  tx_active <= 0 ;
               end
            end
         end
      end
   end
end

endmodule

