// // Simple serial port. // // The transmission rate is fixed to 1 Mbps. // // If the output FIFO is full, an output signal is asserted, which // is expected to be used together with DTR to provide blocking I/O // if desired. // module serial #( parameter logic ENABLE_RX_DATA = 1'b1, parameter logic ENABLE_RX_BREAK = 1'b1, parameter logic ENABLE_TX_DATA = 1'b1, parameter logic ENABLE_TX_BREAK = 1'b1, parameter logic BAUDRATE_SETTABLE = 1'b0, parameter [31:0] BAUDRATE = 115200, parameter [31:0] TTY_CLK = 84000000, parameter NCO_BITS = 24, parameter BREAK_BITS = 16 // Bit times for BREAK detect ) ( input tri1 rst_n, input clk, output tty_tx, input tty_rx, input tx_wstrb, input [7:0] tx_data, input tx_break, // BREAK is asserted as long as this is true output tx_full, output tx_empty, input tri0 tx_flush, input rx_rstrb, output [7:0] rx_data, output rx_break, output rx_full, output rx_empty, input tri0 rx_flush, input [31:0] divisor_wdata, // If divisor is settable input tri0 divisor_wstrb, output [31:0] divisor ); `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. // localparam [NCO_BITS-1:0] default_divisor = round_div(BAUDRATE << NCO_BITS, TTY_CLK >> 4) - 1'b1; reg [NCO_BITS-1:0] divisor_q = default_divisor; reg [NCO_BITS-1:0] nco_q; reg tty_clk_en; // tty clock tick (clock enable) assign divisor = divisor_q; always @(posedge clk) { tty_clk_en, nco_q } <= nco_q + divisor_q + 1'b1; always @(negedge rst_n or posedge clk) if (~rst_n) divisor_q <= default_divisor; else if ( BAUDRATE_SETTABLE & divisor_wstrb ) divisor_q <= divisor_wdata[NCO_BITS-1:0]; // // **** Transmitter section **** // reg tx_rdack; wire [7:0] tx_out_data; // // FIFO // generate if (ENABLE_TX_DATA) begin // // Tx FIFO // reg tx_wstrb_q; always @(negedge rst_n or posedge clk) if (~rst_n) tx_wstrb_q <= 1'b0; else tx_wstrb_q <= tx_wstrb; fifo txfifo ( .aclr ( ~rst_n ), .clock ( clk ), .data ( tx_data ), .wrreq ( tx_wstrb & ~tx_wstrb_q ), .sclr ( tx_flush ), .empty ( tx_empty ), .full ( tx_full ), .rdreq ( tx_rdack ), .q ( tx_out_data ), .usedw ( ) ); end // if (ENABLE_TX_DATA) else begin assign tx_empty = 1'b1; assign tx_full = 1'b1; assign tx_out_data = 8'hxx; end endgenerate // // Transmitter // generate if (ENABLE_TX_DATA | ENABLE_TX_BREAK) begin reg [3:0] tx_phase; reg [3:0] tx_ctr; reg [9:0] tx_sr = ~10'b0; reg tx_sending_break; always @(negedge rst_n or posedge clk) if (~rst_n) begin tx_phase <= 4'h0; tx_ctr <= 'd0; tx_sr <= 10'h3ff; // Line idle tx_rdack <= 1'b0; tty_tx <= 1'b1; tx_sending_break <= 1'b0; end else begin tx_rdack <= 1'b0; tty_tx <= tx_sr[0] & ~tx_sending_break; if ( tty_clk_en ) begin tx_phase <= tx_phase + 1'b1; if (&tx_phase) begin tx_sr[8:0] <= tx_sr[9:1]; tx_sr[9] <= 1'b1; // Stop bit/idle if (|tx_ctr) begin tx_ctr <= tx_ctr - 1'b1; end else if ( ENABLE_TX_BREAK & (tx_break | tx_sending_break) ) begin // Make sure we get an idle bit after break tx_sending_break <= tx_break; end else if ( ~tx_empty ) begin tx_sr[9:2] <= tx_out_data; tx_sr[1] <= 1'b0; // Start bit // 10 = start bit + data + stop bit tx_ctr <= 'd10; tx_rdack <= 1'b1; // Remove from FIFO end end // if (tx_phase == 4'hF) end // if ( tty_clk_en ) end // else: !if(~rst_n) end // if (ENABLE_TX_DATA | ENABLE_TX_BREAK) else begin assign tty_tx = 1'b1; end // else: !if(ENABLE_TX_DATA | ENABLE_TX_BREAK) endgenerate // // **** Receiver section **** // // // Bit phase counter // generate if (ENABLE_RX_DATA | ENABLE_RX_BREAK) begin reg [3:0] rx_phase; reg rx_phase_start = 1'b0; always @(negedge rst_n or posedge clk) if (~rst_n) rx_phase <= 4'b0; else if ( rx_phase_start ) rx_phase <= 4'h8; // Start bit flank detected else rx_phase <= rx_phase + tty_clk_en; end // if (ENABLE_RX_DATA | ENABLE_RX_BREAK) endgenerate // // BREAK detect // generate if (ENABLE_RX_BREAK) begin reg [BREAK_BITS-1:0] rx_break_ctr; wire [BREAK_BITS:0] rx_break_ctr_next = rx_break_ctr + 1'b1; always @(negedge rst_n or posedge clk) if (~rst_n) begin rx_break_ctr <= 'b0; rx_break <= 1'b0; end else begin if ( tty_clk_en ) begin if ( tty_rx ) begin // Rx high = not a break rx_break_ctr <= 'b0; rx_break <= 1'b0; end else if ( &rx_phase ) begin rx_break_ctr <= rx_break_ctr_next; rx_break <= rx_break | rx_break_ctr_next[BREAK_BITS]; end // else: !if(tty_rxd) end // if ( tty_clk_en ) end // else: !if(~rst_n) end // if (ENABLE_RX_BREAK) else begin assign rx_break = 1'b0; end endgenerate // // Data receive // generate if (ENABLE_RX_DATA) begin reg rx_recv_strb; reg rx_rstrb_q; fifo rxfifo ( .aclr ( ~rst_n ), .clock ( clk ), .rdreq ( ~rx_rstrb & rx_rstrb_q ), // Advance after read .q ( rx_data ), .sclr ( rx_flush ), .empty ( rx_empty ), .full ( rx_full ), .wrreq ( rx_recv_strb ), .data ( rx_sr ), .usedw ( ) ); reg [3:0] rx_ctr; reg [2:0] rx_bits; reg [7:0] rx_sr; reg tty_clk_en_q; always @(negedge rst_n or posedge clk) if (~rst_n) begin rx_ctr <= 4'b0; rx_bits <= 3'b111; rx_sr <= 8'hxx; tty_clk_en_q <= 1'b0; rx_rstrb_q <= 1'b0; rx_recv_strb <= 1'b0; rx_phase_start <= 1'b0; end else begin rx_rstrb_q <= rx_rstrb; tty_clk_en_q <= tty_clk_en; rx_recv_strb <= 1'b0; if ( tty_clk_en ) rx_bits <= { rx_bits[1:0], tty_rx }; if ( tty_clk_en_q ) begin if ( !rx_ctr ) begin if ( rx_bits == 3'b100 ) begin rx_ctr <= 4'd10; rx_phase_start <= 1'b1; end end else if ( !rx_phase ) begin rx_sr[6:0] <= rx_sr[7:1]; // Majority vote of three samples rx_sr[7] <= (rx_bits[1] & rx_bits[0]) | (rx_bits[2] & rx_bits[0]) | (rx_bits[2] & rx_bits[1]); rx_ctr <= rx_ctr - 1'b1; // Push to FIFO if this was the bit before stop rx_recv_strb <= rx_ctr == 4'd2; end // if ( &rx_phase ) end // if ( tty_clk_en_q ) end // else: !if(~rst_n) end // if (ENABLE_RX_DATA) endgenerate endmodule // serial