// // Very simple asynchronous tty. Not really a "UART" since it isn't // very "universal". It doesn't have to be. // // Currently only transmit is supported, 8-N-1, at a fixed speed. // The baud rate is tty_clk/((divisor+1)*16). // // Currently there is no overrun checking or interrupt support, but that // can come later. A large FIFO makes that less of an issue anyway... // module tty ( input rst_n, input clk, input valid, input [3:0] wstrb, input [31:0] wdata, input [0:0] addr, output tty_txd ); `include "functions.sv" // For ModelSim // // Baud rate generator; produces a clock enable synchronous // with clk. This is based on a numerically controlled oscillator // (NCO); the baudrate is given as a binary fraction of the input // clock rate divided by the oversampling rate (16); for practical // reasons represented minus one LSB so 0x0.ffffff -> 1 // and 0 -> 0x0.000001. // // The term "divisor" here is probably misleading, since it is // actually a fixed-point *multiplier* which is <= 1. // parameter [31:0] BAUDRATE = 115200; parameter [31:0] TTY_CLK = 84000000; parameter NCO_BITS = 24; reg [NCO_BITS-1:0] divisor = round_div(BAUDRATE << NCO_BITS, TTY_CLK >> 4) - 1'b1; reg [NCO_BITS-1:0] nco_q; reg tty_clk_en; // tty clock tick (clock enable) always @(posedge clk) { tty_clk_en, nco_q } <= nco_q + divisor + 1'b1; // // Tx FIFO // reg tx_rdack; wire tx_wrreq; wire tx_rdempty; wire [7:0] tx_data; fifo txfifo ( .aclr ( ~rst_n ), .clock ( clk ), .data ( wdata ), .rdreq ( tx_rdack ), .sclr ( 1'b0 ), // Flush FIFO command .wrreq ( tx_wrreq ), .empty ( tx_rdempty ), .full ( ), .q ( tx_data ), .usedw ( ) ); // // CPU -> transmitter data. No wait state is needed nor expected. // // Data mask (if/when needed) wire [31:0] wmask = {{8{wstrb[3]}}, {8{wstrb[2]}}, {8{wstrb[1]}}, {8{wstrb[0]}}}; // Data (FIFO) register; normally addressed as byte // Protect against long pulses (edge detect) reg old_wstrb; always @(posedge clk) old_wstrb <= wstrb[0]; assign tx_wrreq = valid & wstrb[0] & ~old_wstrb & (addr == 0); // Divisor register always @(posedge clk) if (wstrb[0] & valid & (addr == 1)) divisor <= wdata[NCO_BITS-1:0] & wmask; // // Transmitter // reg [3:0] tx_phase; reg [3:0] tx_bits; reg [9:0] tx_sr = ~10'b0; // Shift register assign tty_txd = tx_sr[0]; always @(negedge rst_n or posedge clk) if (~rst_n) begin tx_phase <= 4'h0; tx_bits <= 4'd0; tx_sr <= ~10'b0; // Line idle tx_rdack <= 1'b0; end else begin tx_rdack <= 1'b0; if ( tty_clk_en ) begin tx_phase <= tx_phase + 1'b1; if (tx_phase == 4'hF) begin tx_sr[8:0] <= tx_sr[9:1]; tx_sr[9] <= 1'b1; // Stop bit/idle if (tx_bits == 4'd0) begin if ( ~tx_rdempty ) begin tx_sr[9:2] <= tx_data; tx_sr[1] <= 1'b0; // Start bit // 10 = start bit + data + stop bit tx_bits <= 4'd10; tx_rdack <= 1'b1; // Remove from FIFO end end else begin tx_bits <= tx_bits - 1'b1; end // else: !if(tx_bits == 4'd0) end // if (tx_phase == 4'hF) end // if ( tty_clk_en ) end // else: !if(~rst_n) endmodule // tty