URL
https://opencores.org/ocsvn/openarty/openarty/trunk
Subversion Repositories openarty
[/] [openarty/] [trunk/] [rtl/] [wbuart.v] - Rev 50
Compare with Previous | Blame | View Log
//////////////////////////////////////////////////////////////////////////////// // // Filename: wbuart.v // // Project: wbuart32, a full featured UART with simulator // // Purpose: Unlilke wbuart-insert.v, this is a full blown wishbone core // with integrated FIFO support to support the UART transmitter // and receiver found within here. As a result, it's usage may be // heavier on the bus than the insert, but it may also be more useful. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2015-2016, Gisselquist Technology, LLC // // This program is free software (firmware): you can redistribute it and/or // modify it under the terms of the GNU 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 MERCHANTIBILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program. (It's in the $(ROOT)/doc directory. Run make with no // target there if the PDF file isn't present.) If not, see // <http://www.gnu.org/licenses/> for a copy. // // License: GPL, v3, as defined and found on www.gnu.org, // http://www.gnu.org/licenses/gpl.html // // //////////////////////////////////////////////////////////////////////////////// // // `define UART_SETUP 2'b00 `define UART_FIFO 2'b01 `define UART_RXREG 2'b10 `define UART_TXREG 2'b11 module wbuart(i_clk, i_rst, // i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, o_wb_ack, o_wb_stall, o_wb_data, // i_uart_rx, o_uart_tx, i_cts_n, o_rts_n, // o_uart_rx_int, o_uart_tx_int, o_uart_rxfifo_int, o_uart_txfifo_int); parameter [30:0] INITIAL_SETUP = 31'd25; // 4MB 8N1, when using 100MHz clock parameter [3:0] LGFLEN = 4; parameter [0:0] HARDWARE_FLOW_CONTROL_PRESENT = 1'b1; // Perform a simple/quick bounds check on the log FIFO length, to make // sure its within the bounds we can support with our current // interface. localparam [3:0] LCLLGFLEN = (LGFLEN > 4'ha)? 4'ha : ((LGFLEN < 4'h2) ? 4'h2 : LGFLEN); // input i_clk, i_rst; // Wishbone inputs input i_wb_cyc, i_wb_stb, i_wb_we; input [1:0] i_wb_addr; input [31:0] i_wb_data; output reg o_wb_ack; output wire o_wb_stall; output reg [31:0] o_wb_data; // input i_uart_rx; output wire o_uart_tx; // RTS is used for hardware flow control. According to Wikipedia, it // should probably be renamed RTR for "ready to receive". It tell us // whether or not the receiving hardware is ready to accept another // byte. If low, the transmitter will pause. // // If you don't wish to use hardware flow control, just set i_cts_n to // 1'b0 and let the optimizer simply remove this logic. input i_cts_n; // CTS is the "Clear-to-send" signal. We set it anytime our FIFO // isn't full. Feel free to ignore this output if you do not wish to // use flow control. output reg o_rts_n; output wire o_uart_rx_int, o_uart_tx_int, o_uart_rxfifo_int, o_uart_txfifo_int; wire tx_busy; // // The UART setup parameters: bits per byte, stop bits, parity, and // baud rate are all captured within this uart_setup register. // reg [30:0] uart_setup; initial uart_setup = INITIAL_SETUP; always @(posedge i_clk) // Under wishbone rules, a write takes place any time i_wb_stb // is high. If that's the case, and if the write was to the // setup address, then set us up for the new parameters. if ((i_wb_stb)&&(i_wb_addr == `UART_SETUP)&&(i_wb_we)) uart_setup <= { (i_wb_data[30]) ||(!HARDWARE_FLOW_CONTROL_PRESENT), i_wb_data[29:0] }; ///////////////////////////////////////// // // // First, the UART receiver // // ///////////////////////////////////////// // First the wires/registers this receiver depends upon wire rx_stb, rx_break, rx_perr, rx_ferr, ck_uart; wire [7:0] rx_uart_data; reg rx_uart_reset; // Here's our UART receiver. Basically, it accepts our setup wires, // the UART input, a clock, and a reset line, and produces outputs: // a stb (true when new data is ready), and an 8-bit data out value // valid when stb is high. `ifdef USE_LITE_UART rxuartlite #(INITIAL_SETUP[23:0]) rx(i_clk, (i_rst), i_uart_rx, rx_stb, rx_uart_data); assign rx_break = 1'b0; assign rx_perr = 1'b0; assign rx_ferr = 1'b0; assign ck_uart = 1'b0; `else // The full receiver also produces a break value (true during a break // cond.), and parity/framing error flags--also valid when stb is true. rxuart #(INITIAL_SETUP) rx(i_clk, (i_rst)||(rx_uart_reset), uart_setup, i_uart_rx, rx_stb, rx_uart_data, rx_break, rx_perr, rx_ferr, ck_uart); // The real trick is ... now that we have this extra data, what do we do // with it? `endif // We place it into a receiver FIFO. // // Here's the declarations for the wires it needs. wire rx_empty_n, rx_fifo_err; wire [7:0] rxf_wb_data; wire [15:0] rxf_status; reg rxf_wb_read; // // And here's the FIFO proper. // // Note that the FIFO will be cleared upon any reset: either if there's // a UART break condition on the line, the receiver is in reset, or an // external reset is issued. // // The FIFO accepts strobe and data from the receiver. // We issue another wire to it (rxf_wb_read), true when we wish to read // from the FIFO, and we get our data in rxf_wb_data. The FIFO outputs // four status-type values: 1) is it non-empty, 2) is the FIFO over half // full, 3) a 16-bit status register, containing info regarding how full // the FIFO truly is, and 4) an error indicator. ufifo #(.LGFLEN(LCLLGFLEN), .RXFIFO(1)) rxfifo(i_clk, (i_rst)||(rx_break)||(rx_uart_reset), rx_stb, rx_uart_data, rx_empty_n, rxf_wb_read, rxf_wb_data, rxf_status, rx_fifo_err); assign o_uart_rxfifo_int = rxf_status[1]; // We produce four interrupts. One of the receive interrupts indicates // whether or not the receive FIFO is non-empty. This should wake up // the CPU. assign o_uart_rx_int = rxf_status[0]; // The clear to send line, which may be ignored, but which we set here // to be true any time the FIFO has fewer than N-2 items in it. // Why N-1? Because at N-1 we are totally full, but already so full // that if the transmit end starts sending we won't have a location to // receive it. (Transmit might've started on the next character by the // time we set this--need to set it to one character before necessary always @(posedge i_clk) o_rts_n = ((HARDWARE_FLOW_CONTROL_PRESENT) &&(!uart_setup[30]) &&(rxf_status[(LCLLGFLEN+1):4]=={(LCLLGFLEN-2){1'b1}})); // If the bus requests that we read from the receive FIFO, we need to // tell this to the receive FIFO. Note that because we are using a // clock here, the output from the receive FIFO will necessarily be // delayed by an extra clock. initial rxf_wb_read = 1'b0; always @(posedge i_clk) rxf_wb_read <= (i_wb_stb)&&(i_wb_addr[1:0]==`UART_RXREG) &&(!i_wb_we); // Now, let's deal with those RX UART errors: both the parity and frame // errors. As you may recall, these are valid only when rx_stb is // valid, so we need to hold on to them until the user reads them via // a UART read request.. reg r_rx_perr, r_rx_ferr; initial r_rx_perr = 1'b0; initial r_rx_ferr = 1'b0; always @(posedge i_clk) if ((rx_uart_reset)||(rx_break)) begin // Clear the error r_rx_perr <= 1'b0; r_rx_ferr <= 1'b0; end else if ((i_wb_stb) &&(i_wb_addr[1:0]==`UART_RXREG)&&(i_wb_we)) begin // Reset the error lines if a '1' is ever written to // them, otherwise leave them alone. // r_rx_perr <= (r_rx_perr)&&(~i_wb_data[9]); r_rx_ferr <= (r_rx_ferr)&&(~i_wb_data[10]); end else if (rx_stb) begin // On an rx_stb, capture any parity or framing error // indications. These aren't kept with the data rcvd, // but rather kept external to the FIFO. As a result, // if you get a parity or framing error, you will never // know which data byte it was associated with. // For now ... that'll work. r_rx_perr <= (r_rx_perr)||(rx_perr); r_rx_ferr <= (r_rx_ferr)||(rx_ferr); end initial rx_uart_reset = 1'b1; always @(posedge i_clk) if ((i_rst)||((i_wb_stb)&&(i_wb_addr[1:0]==`UART_SETUP)&&(i_wb_we))) // The receiver reset, always set on a master reset // request. rx_uart_reset <= 1'b1; else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_RXREG)&&(i_wb_we)) // Writes to the receive register will command a receive // reset anytime bit[12] is set. rx_uart_reset <= i_wb_data[12]; else rx_uart_reset <= 1'b0; // Finally, we'll construct a 32-bit value from these various wires, // to be returned over the bus on any read. These include the data // that would be read from the FIFO, an error indicator set upon // reading from an empty FIFO, a break indicator, and the frame and // parity error signals. wire [31:0] wb_rx_data; assign wb_rx_data = { 16'h00, 3'h0, rx_fifo_err, rx_break, rx_ferr, r_rx_perr, !rx_empty_n, rxf_wb_data}; ///////////////////////////////////////// // // // Then the UART transmitter // // ///////////////////////////////////////// wire tx_empty_n, txf_err, tx_break; wire [7:0] tx_data; wire [15:0] txf_status; reg txf_wb_write, tx_uart_reset; reg [7:0] txf_wb_data; // Unlike the receiver which goes from RXUART -> UFIFO -> WB, the // transmitter basically goes WB -> UFIFO -> TXUART. Hence, to build // support for the transmitter, we start with the command to write data // into the FIFO. In this case, we use the act of writing to the // UART_TXREG address as our indication that we wish to write to the // FIFO. Here, we create a write command line, and latch the data for // the extra clock that it'll take so that the command and data can be // both true on the same clock. initial txf_wb_write = 1'b0; always @(posedge i_clk) begin txf_wb_write <= (i_wb_stb)&&(i_wb_addr == `UART_TXREG) &&(i_wb_we); txf_wb_data <= i_wb_data[7:0]; end // Transmit FIFO // // Most of this is just wire management. The TX FIFO is identical in // implementation to the RX FIFO (theyre both UFIFOs), but the TX // FIFO is fed from the WB and read by the transmitter. Some key // differences to note: we reset the transmitter on any request for a // break. We read from the FIFO any time the UART transmitter is idle. // and ... we just set the values (above) for controlling writing into // this. ufifo #(.LGFLEN(LGFLEN), .RXFIFO(0)) txfifo(i_clk, (tx_break)||(tx_uart_reset), txf_wb_write, txf_wb_data, tx_empty_n, (!tx_busy)&&(tx_empty_n), tx_data, txf_status, txf_err); // Let's create two transmit based interrupts from the FIFO for the CPU. // The first will be true any time the FIFO has at least one open // position within it. assign o_uart_tx_int = txf_status[0]; // The second will be true any time the FIFO is less than half // full, allowing us a change to always keep it (near) fully // charged. assign o_uart_txfifo_int = txf_status[1]; `ifndef USE_LITE_UART // Break logic // // A break in a UART controller is any time the UART holds the line // low for an extended period of time. Here, we capture the wb_data[9] // wire, on writes, as an indication we wish to break. As long as you // write unsigned characters to the interface, this will never be true // unless you wish it to be true. Be aware, though, writing a valid // value to the interface will bring it out of the break condition. reg r_tx_break; initial r_tx_break = 1'b0; always @(posedge i_clk) if (i_rst) r_tx_break <= 1'b0; else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_TXREG)&&(i_wb_we)) r_tx_break <= i_wb_data[9]; assign tx_break = r_tx_break; `else assign tx_break = 1'b0; `endif // TX-Reset logic // // This is nearly identical to the RX reset logic above. Basically, // any time someone writes to bit [12] the transmitter will go through // a reset cycle. Keep bit [12] low, and everything will proceed as // normal. initial tx_uart_reset = 1'b1; always @(posedge i_clk) if((i_rst)||((i_wb_stb)&&(i_wb_addr == `UART_SETUP)&&(i_wb_we))) tx_uart_reset <= 1'b1; else if ((i_wb_stb)&&(i_wb_addr[1:0]==`UART_TXREG)&&(i_wb_we)) tx_uart_reset <= i_wb_data[12]; else tx_uart_reset <= 1'b0; `ifdef USE_LITE_UART txuart #(INITIAL_SETUP[23:0]) tx(i_clk, (tx_empty_n), tx_data, o_uart_tx, tx_busy); `else wire cts_n; assign cts_n = (HARDWARE_FLOW_CONTROL_PRESENT)&&(i_cts_n); // Finally, the UART transmitter module itself. Note that we haven't // connected the reset wire. Transmitting is as simple as setting // the stb value (here set to tx_empty_n) and the data. When these // are both set on the same clock that tx_busy is low, the transmitter // will move on to the next data byte. Really, the only thing magical // here is that tx_empty_n wire--thus, if there's anything in the FIFO, // we read it here. (You might notice above, we register a read any // time (tx_empty_n) and (!tx_busy) are both true---the condition for // starting to transmit a new byte.) txuart #(INITIAL_SETUP) tx(i_clk, 1'b0, uart_setup, r_tx_break, (tx_empty_n), tx_data, cts_n, o_uart_tx, tx_busy); `endif // Now that we are done with the chain, pick some wires for the user // to read on any read of the transmit port. // // This port is different from reading from the receive port, since // there are no side effects. (Reading from the receive port advances // the receive FIFO, here only writing to the transmit port advances the // transmit FIFO--hence the read values are free for ... whatever.) // We choose here to provide information about the transmit FIFO // (txf_err, txf_half_full, txf_full_n), information about the current // voltage on the line (o_uart_tx)--and even the voltage on the receive // line (ck_uart), as well as our current setting of the break and // whether or not we are actively transmitting. wire [31:0] wb_tx_data; assign wb_tx_data = { 16'h00, i_cts_n, txf_status[1:0], txf_err, ck_uart, o_uart_tx, tx_break, (tx_busy|txf_status[0]), (tx_busy|txf_status[0])?txf_wb_data:8'b00}; // Each of the FIFO's returns a 16 bit status value. This value tells // us both how big the FIFO is, as well as how much of the FIFO is in // use. Let's merge those two status words together into a word we // can use when reading about the FIFO. wire [31:0] wb_fifo_data; assign wb_fifo_data = { txf_status, rxf_status }; // You may recall from above that reads take two clocks. Hence, we // need to delay the address decoding for a clock until the data is // ready. We do that here. reg [1:0] r_wb_addr; always @(posedge i_clk) r_wb_addr <= i_wb_addr; // Likewise, the acknowledgement is delayed by one clock. reg r_wb_ack; always @(posedge i_clk) // We'll ACK in two clocks r_wb_ack <= i_wb_stb; always @(posedge i_clk) // Okay, time to set the ACK o_wb_ack <= r_wb_ack; // Finally, set the return data. This data must be valid on the same // clock o_wb_ack is high. On all other clocks, it is irrelelant--since // no one cares, no one is reading it, it gets lost in the mux in the // interconnect, etc. For this reason, we can just simplify our logic. always @(posedge i_clk) casez(r_wb_addr) `UART_SETUP: o_wb_data <= { 1'b0, uart_setup }; `UART_FIFO: o_wb_data <= wb_fifo_data; `UART_RXREG: o_wb_data <= wb_rx_data; `UART_TXREG: o_wb_data <= wb_tx_data; endcase // This device never stalls. Sure, it takes two clocks, but they are // pipelined, and nothing stalls that pipeline. (Creates FIFO errors, // perhaps, but doesn't stall the pipeline.) Hence, we can just // set this value to zero. assign o_wb_stall = 1'b0; endmodule