aboutsummaryrefslogtreecommitdiff
path: root/src/rtl/uart_core.v
diff options
context:
space:
mode:
Diffstat (limited to 'src/rtl/uart_core.v')
-rw-r--r--src/rtl/uart_core.v586
1 files changed, 586 insertions, 0 deletions
diff --git a/src/rtl/uart_core.v b/src/rtl/uart_core.v
new file mode 100644
index 0000000..5596958
--- /dev/null
+++ b/src/rtl/uart_core.v
@@ -0,0 +1,586 @@
+//======================================================================
+//
+// uart_core.v
+// -----------
+// A simple universal asynchronous receiver/transmitter (UART)
+// interface. The interface contains 16 byte wide transmit and
+// receivea buffers and can handle start and stop bits. But in
+// general is rather simple. The primary purpose is as host
+// interface for the coretest design. The core also has a
+// loopback mode to allow testing of a serial link.
+//
+// Note that the UART has a separate API interface to allow
+// a control core to change settings such as speed. But the core
+// has default values to allow it to start operating directly
+// after reset. No config should be needed.
+//
+//
+// Author: Joachim Strombergson
+// Copyright (c) 2014 SUNET
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in
+// the documentation and/or other materials provided with the
+// distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//======================================================================
+
+module uart_core(
+ input wire clk,
+ input wire reset_n,
+
+ // Configuration parameters
+ input wire [15 : 0] bit_rate,
+ input wire [1 : 0] stop_bits,
+
+ // External data interface
+ input wire rxd,
+ output wire txd,
+
+ // Internal receive interface.
+ output wire rxd_syn,
+ output [7 : 0] rxd_data,
+ input wire rxd_ack,
+
+ // Internal transmit interface.
+ input wire txd_syn,
+ input wire [7 : 0] txd_data,
+ output wire txd_ack
+ );
+
+
+ //----------------------------------------------------------------
+ // Internal constant and parameter definitions.
+ //----------------------------------------------------------------
+ // The default clock rate is based on target clock frequency
+ // divided by the bit rate times in order to hit the
+ // center of the bits. I.e.
+ // Clock: 50 MHz
+ // Bitrate: 19200 bps
+ // Divisor = 50*10E6 / 9600 = 5208
+ parameter DEFAULT_CLK_RATE = 5208;
+ parameter DEFAULT_HALF_CLK_RATE = DEFAULT_CLK_RATE / 2;
+
+ parameter DEFAULT_DATA_BITS = 8;
+ parameter DEFAULT_STOP_BITS = 1;
+
+ parameter ERX_IDLE = 0;
+ parameter ERX_START = 1;
+ parameter ERX_BITS = 2;
+ parameter ERX_STOP = 3;
+ parameter ERX_SYN = 4;
+
+ parameter ETX_IDLE = 0;
+ parameter ETX_ACK = 1;
+ parameter ETX_START = 2;
+ parameter ETX_BITS = 3;
+ parameter ETX_STOP = 4;
+
+
+ //----------------------------------------------------------------
+ // Registers including update variables and write enable.
+ //----------------------------------------------------------------
+ reg rxd_reg;
+
+ reg [7 : 0] rxd_byte_reg;
+ reg rxd_byte_we;
+
+ reg [4 : 0] rxd_bit_ctr_reg;
+ reg [4 : 0] rxd_bit_ctr_new;
+ reg rxd_bit_ctr_we;
+ reg rxd_bit_ctr_rst;
+ reg rxd_bit_ctr_inc;
+
+ reg [15 : 0] rxd_bitrate_ctr_reg;
+ reg [15 : 0] rxd_bitrate_ctr_new;
+ reg rxd_bitrate_ctr_we;
+ reg rxd_bitrate_ctr_rst;
+ reg rxd_bitrate_ctr_inc;
+
+ reg rxd_syn_reg;
+ reg rxd_syn_new;
+ reg rxd_syn_we;
+
+ reg [2 : 0] erx_ctrl_reg;
+ reg [2 : 0] erx_ctrl_new;
+ reg erx_ctrl_we;
+
+ reg txd_reg;
+ reg txd_new;
+ reg txd_we;
+
+ reg [7 : 0] txd_byte_reg;
+ reg [7 : 0] txd_byte_new;
+ reg txd_byte_we;
+
+ reg [4 : 0] txd_bit_ctr_reg;
+ reg [4 : 0] txd_bit_ctr_new;
+ reg txd_bit_ctr_we;
+ reg txd_bit_ctr_rst;
+ reg txd_bit_ctr_inc;
+
+ reg [15 : 0] txd_bitrate_ctr_reg;
+ reg [15 : 0] txd_bitrate_ctr_new;
+ reg txd_bitrate_ctr_we;
+ reg txd_bitrate_ctr_rst;
+ reg txd_bitrate_ctr_inc;
+
+ reg txd_ack_reg;
+ reg txd_ack_new;
+ reg txd_ack_we;
+
+ reg [2 : 0] etx_ctrl_reg;
+ reg [2 : 0] etx_ctrl_new;
+ reg etx_ctrl_we;
+
+
+ //----------------------------------------------------------------
+ // Wires.
+ //----------------------------------------------------------------
+
+
+ //----------------------------------------------------------------
+ // Concurrent connectivity for ports etc.
+ //----------------------------------------------------------------
+ assign txd = txd_reg;
+ assign rxd_syn = rxd_syn_reg;
+ assign rxd_data = rxd_byte_reg;
+ assign txd_ack = txd_ack_reg;
+
+
+ //----------------------------------------------------------------
+ // reg_update
+ //
+ // Update functionality for all registers in the core.
+ // All registers are positive edge triggered with synchronous
+ // active low reset. All registers have write enable.
+ //----------------------------------------------------------------
+ always @ (posedge clk)
+ begin: reg_update
+ if (!reset_n)
+ begin
+ rxd_reg <= 0;
+ rxd_byte_reg <= 8'h00;
+ rxd_bit_ctr_reg <= 4'h0;
+ rxd_bitrate_ctr_reg <= 16'h0000;
+ rxd_syn_reg <= 0;
+ erx_ctrl_reg <= ERX_IDLE;
+
+ txd_reg <= 1;
+ txd_byte_reg <= 8'h00;
+ txd_bit_ctr_reg <= 4'h0;
+ txd_bitrate_ctr_reg <= 16'h0000;
+ txd_ack_reg <= 0;
+ etx_ctrl_reg <= ETX_IDLE;
+ end
+ else
+ begin
+ // We sample the rx input port every cycle.
+ rxd_reg <= rxd;
+
+ // We shift the rxd bit into msb.
+ if (rxd_byte_we)
+ begin
+ rxd_byte_reg <= {rxd_reg, rxd_byte_reg[7 : 1]};
+ end
+
+ if (rxd_bit_ctr_we)
+ begin
+ rxd_bit_ctr_reg <= rxd_bit_ctr_new;
+ end
+
+ if (rxd_bitrate_ctr_we)
+ begin
+ rxd_bitrate_ctr_reg <= rxd_bitrate_ctr_new;
+ end
+
+ if (rxd_syn_we)
+ begin
+ rxd_syn_reg = rxd_syn_new;
+ end
+
+ if (erx_ctrl_we)
+ begin
+ erx_ctrl_reg <= erx_ctrl_new;
+ end
+
+ if (txd_we)
+ begin
+ txd_reg = txd_new;
+ end
+
+ if (txd_byte_we)
+ begin
+ txd_byte_reg = txd_byte_new;
+ end
+
+ if (txd_bit_ctr_we)
+ begin
+ txd_bit_ctr_reg <= txd_bit_ctr_new;
+ end
+
+ if (txd_bitrate_ctr_we)
+ begin
+ txd_bitrate_ctr_reg <= txd_bitrate_ctr_new;
+ end
+
+ if (txd_ack_we)
+ begin
+ txd_ack_reg = txd_ack_new;
+ end
+
+ if (etx_ctrl_we)
+ begin
+ etx_ctrl_reg <= etx_ctrl_new;
+ end
+ end
+ end // reg_update
+
+
+ //----------------------------------------------------------------
+ // rxd_bit_ctr
+ //
+ // Bit counter for receiving data on the external
+ // serial interface.
+ //----------------------------------------------------------------
+ always @*
+ begin: rxd_bit_ctr
+ rxd_bit_ctr_new = 4'h0;
+ rxd_bit_ctr_we = 0;
+
+ if (rxd_bit_ctr_rst)
+ begin
+ rxd_bit_ctr_new = 4'h0;
+ rxd_bit_ctr_we = 1;
+ end
+
+ else if (rxd_bit_ctr_inc)
+ begin
+ rxd_bit_ctr_new = rxd_bit_ctr_reg + 4'b0001;
+ rxd_bit_ctr_we = 1;
+ end
+ end // rxd_bit_ctr
+
+
+ //----------------------------------------------------------------
+ // rxd_bitrate_ctr
+ //
+ // Bitrate counter for receiving data on the external
+ // serial interface.
+ //----------------------------------------------------------------
+ always @*
+ begin: rxd_bitrate_ctr
+ rxd_bitrate_ctr_new = 16'h0000;
+ rxd_bitrate_ctr_we = 0;
+
+ if (rxd_bitrate_ctr_rst)
+ begin
+ rxd_bitrate_ctr_new = 16'h0000;
+ rxd_bitrate_ctr_we = 1;
+ end
+
+ else if (rxd_bitrate_ctr_inc)
+ begin
+ rxd_bitrate_ctr_new = rxd_bitrate_ctr_reg + 16'h0001;
+ rxd_bitrate_ctr_we = 1;
+ end
+ end // rxd_bitrate_ctr
+
+
+
+ //----------------------------------------------------------------
+ // txd_bit_ctr
+ //
+ // Bit counter for transmitting data on the external
+ // serial interface.
+ //----------------------------------------------------------------
+ always @*
+ begin: txd_bit_ctr
+ txd_bit_ctr_new = 4'h0;
+ txd_bit_ctr_we = 0;
+
+ if (txd_bit_ctr_rst)
+ begin
+ txd_bit_ctr_new = 4'h0;
+ txd_bit_ctr_we = 1;
+ end
+
+ else if (txd_bit_ctr_inc)
+ begin
+ txd_bit_ctr_new = txd_bit_ctr_reg + 4'b0001;
+ txd_bit_ctr_we = 1;
+ end
+ end // txd_bit_ctr
+
+
+ //----------------------------------------------------------------
+ // txd_bitrate_ctr
+ //
+ // Bitrate counter for transmitting data on the external
+ // serial interface.
+ //----------------------------------------------------------------
+ always @*
+ begin: txd_bitrate_ctr
+ txd_bitrate_ctr_new = 16'h0000;
+ txd_bitrate_ctr_we = 0;
+
+ if (txd_bitrate_ctr_rst)
+ begin
+ txd_bitrate_ctr_new = 16'h0000;
+ txd_bitrate_ctr_we = 1;
+ end
+
+ else if (txd_bitrate_ctr_inc)
+ begin
+ txd_bitrate_ctr_new = txd_bitrate_ctr_reg + 16'h0001;
+ txd_bitrate_ctr_we = 1;
+ end
+ end // txd_bitrate_ctr
+
+
+ //----------------------------------------------------------------
+ // external_rx_engine
+ //
+ // Logic that implements the receive engine towards
+ // the external interface. Detects incoming data, collects it,
+ // if required checks parity and store correct data into
+ // the rx buffer.
+ //----------------------------------------------------------------
+ always @*
+ begin: external_rx_engine
+ rxd_bit_ctr_rst = 0;
+ rxd_bit_ctr_inc = 0;
+ rxd_bitrate_ctr_rst = 0;
+ rxd_bitrate_ctr_inc = 0;
+ rxd_byte_we = 0;
+ rxd_syn_new = 0;
+ rxd_syn_we = 0;
+ erx_ctrl_new = ERX_IDLE;
+ erx_ctrl_we = 0;
+
+ case (erx_ctrl_reg)
+ ERX_IDLE:
+ begin
+ if (!rxd_reg)
+ begin
+ // Possible start bit detected.
+ rxd_bitrate_ctr_rst = 1;
+ erx_ctrl_new = ERX_START;
+ erx_ctrl_we = 1;
+ end
+ end
+
+
+ ERX_START:
+ begin
+ rxd_bitrate_ctr_inc = 1;
+ if (rxd_reg)
+ begin
+ // Just a glitch.
+ erx_ctrl_new = ERX_IDLE;
+ erx_ctrl_we = 1;
+ end
+ else
+ begin
+ if (rxd_bitrate_ctr_reg == DEFAULT_HALF_CLK_RATE)
+ begin
+ // start bit assumed. We start sampling data.
+ rxd_bit_ctr_rst = 1;
+ rxd_bitrate_ctr_rst = 1;
+ erx_ctrl_new = ERX_BITS;
+ erx_ctrl_we = 1;
+ end
+ end
+ end
+
+
+ ERX_BITS:
+ begin
+ if (rxd_bitrate_ctr_reg < DEFAULT_CLK_RATE)
+ begin
+ rxd_bitrate_ctr_inc = 1;
+ end
+ else
+ begin
+ rxd_byte_we = 1;
+ rxd_bit_ctr_inc = 1;
+ rxd_bitrate_ctr_rst = 1;
+ if (rxd_bit_ctr_reg == DEFAULT_DATA_BITS - 1)
+ begin
+ erx_ctrl_new = ERX_STOP;
+ erx_ctrl_we = 1;
+ end
+ end
+ end
+
+
+ ERX_STOP:
+ begin
+ rxd_bitrate_ctr_inc = 1;
+ if (rxd_bitrate_ctr_reg == DEFAULT_CLK_RATE * DEFAULT_STOP_BITS)
+ begin
+ rxd_syn_new = 1;
+ rxd_syn_we = 1;
+ erx_ctrl_new = ERX_SYN;
+ erx_ctrl_we = 1;
+ end
+ end
+
+
+ ERX_SYN:
+ begin
+ if (rxd_ack)
+ begin
+ rxd_syn_new = 0;
+ rxd_syn_we = 1;
+ erx_ctrl_new = ERX_IDLE;
+ erx_ctrl_we = 1;
+ end
+ end
+
+
+ default:
+ begin
+
+ end
+ endcase // case (erx_ctrl_reg)
+ end // external_rx_engine
+
+
+ //----------------------------------------------------------------
+ // external_tx_engine
+ //
+ // Logic that implements the transmit engine towards
+ // the external interface.
+ //----------------------------------------------------------------
+ always @*
+ begin: external_tx_engine
+ txd_new = 0;
+ txd_we = 0;
+ txd_byte_new = 0;
+ txd_byte_we = 0;
+ txd_bit_ctr_rst = 0;
+ txd_bit_ctr_inc = 0;
+ txd_bitrate_ctr_rst = 0;
+ txd_bitrate_ctr_inc = 0;
+ txd_ack_new = 0;
+ txd_ack_we = 0;
+ etx_ctrl_new = ETX_IDLE;
+ etx_ctrl_we = 0;
+
+ case (etx_ctrl_reg)
+ ETX_IDLE:
+ begin
+ txd_new = 1;
+ txd_we = 1;
+ if (txd_syn)
+ begin
+ txd_byte_new = txd_data;
+ txd_byte_we = 1;
+ txd_ack_new = 1;
+ txd_ack_we = 1;
+ txd_bitrate_ctr_rst = 1;
+ etx_ctrl_new = ETX_ACK;
+ etx_ctrl_we = 1;
+ end
+ end
+
+
+ ETX_ACK:
+ begin
+ if (!txd_syn)
+ begin
+ txd_new = 0;
+ txd_we = 1;
+ txd_ack_new = 0;
+ txd_ack_we = 1;
+ etx_ctrl_new = ETX_START;
+ etx_ctrl_we = 1;
+ end
+ end
+
+ ETX_START:
+ begin
+ if (txd_bitrate_ctr_reg == DEFAULT_CLK_RATE)
+ begin
+ txd_bit_ctr_rst = 1;
+ etx_ctrl_new = ETX_BITS;
+ etx_ctrl_we = 1;
+ end
+ else
+ begin
+ txd_bitrate_ctr_inc = 1;
+ end
+ end
+
+
+ ETX_BITS:
+ begin
+ if (txd_bitrate_ctr_reg < DEFAULT_CLK_RATE)
+ begin
+ txd_bitrate_ctr_inc = 1;
+ end
+ else
+ begin
+ txd_bitrate_ctr_rst = 1;
+
+ if (txd_bit_ctr_reg == DEFAULT_DATA_BITS)
+ begin
+ txd_new = 1;
+ txd_we = 1;
+ etx_ctrl_new = ETX_STOP;
+ etx_ctrl_we = 1;
+ end
+ else
+ begin
+ txd_new = txd_byte_reg[txd_bit_ctr_reg];
+ txd_we = 1;
+ txd_bit_ctr_inc = 1;
+ end
+ end
+ end
+
+
+ ETX_STOP:
+ begin
+ txd_bitrate_ctr_inc = 1;
+ if (txd_bitrate_ctr_reg == DEFAULT_CLK_RATE * DEFAULT_STOP_BITS)
+ begin
+ etx_ctrl_new = ETX_IDLE;
+ etx_ctrl_we = 1;
+ end
+ end
+
+
+ default:
+ begin
+
+ end
+ endcase // case (etx_ctrl_reg)
+ end // external_tx_engine
+
+endmodule // uart
+
+//======================================================================
+// EOF uart.v
+//======================================================================