// // 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 sys_clk, input tty_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 tty_clk // parameter [31:0] BAUDRATE = 115200; parameter [31:0] TTY_CLK = 48000000; localparam divisor_bits = 16; reg [divisor_bits-1:0] divisor_q = round_div(TTY_CLK, BAUDRATE << 4) - 1; wire [divisor_bits-1:0] divisor; reg [divisor_bits-1:0] clk_div; reg tty_clk_en; // tty clock tick (clock enable) always @(negedge rst_n or posedge tty_clk) if (~rst_n) begin clk_div <= 16; // Give enough time for synchronizer tty_clk_en <= 1'b0; end else begin if (~|clk_div) begin clk_div <= divisor; tty_clk_en <= 1'b1; end else begin clk_div <= clk_div - 1'b1; tty_clk_en <= 1'b0; end end // else: !if(~rst_n) // // Tx FIFO // reg tx_rdack; wire tx_wrreq; wire tx_rdempty; wire [7:0] tx_data; fifo txfifo ( .aclr ( ~rst_n ), .data ( wdata ), .q ( tx_data ), .rdclk ( tty_clk ), .rdreq ( tx_rdack ), .wrclk ( sys_clk ), .wrreq ( tx_wrreq ), .rdempty ( tx_rdempty ), .rdusedw ( ), .wrfull ( ), .wrusedw ( ) ); // // 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 sys_clk) old_wstrb <= wstrb[0]; assign tx_wrreq = valid & wstrb[0] & ~old_wstrb & (addr == 0); // Divisor register always @(posedge sys_clk) if (wstrb[0] & valid & (addr == 1)) divisor_q <= wdata[divisor_bits-1:0] & wmask; synchronizer #(.width(divisor_bits), .stages(2)) sync_divisor ( .rst_n ( rst_n ), .clk ( tty_clk ), .d ( divisor_q ), .q ( divisor ) ); // // 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 tty_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