123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- //
- // 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
|