123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- //
- // 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).
- //
- // The registers are as follows:
- //
- // 0 - WO - data register (will be RW)
- // 1 - RW - baud rate register (binary fraction of TTY_CLK/16 - 1)
- // 2 - RO - status register
- // 0 - transmitter idle (FIFO and shift register empty)
- // 1 - transmit FIFO 3/4 empty
- // 2 - transmit FIFO 1/2 empty
- // 3 - transmit FIFO 1/2 full (inverse of bit 2)
- // 4 - transmit FIFO 3/4 full
- // 3 - RW - interrupt enable register (status register mask)
- module tty (
- input rst_n,
- input clk,
- input valid,
- input [3:0] wstrb,
- input [31:0] wdata,
- input [1:0] addr,
- output reg [31:0] rdata,
- output reg irq,
- 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;
- wire [8:0] tx_usedw;
- 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 ( tx_usedw )
- );
- //
- // 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)
- //
- // CPU interface
- //
- // 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 == 2'b00);
- // Status register definition
- localparam status_bits = 5;
- wire [status_bits-1:0] status;
- assign status[0] = tx_rdempty & (tx_bits == 4'd0);
- assign status[1] = tx_usedw[8:7] == 2'b00;
- assign status[2] = ~tx_usedw[8];
- assign status[3] = tx_usedw[8];
- assign status[4] = tx_usedw[8:7] == 2'b11;
- reg [status_bits-1:0] irq_en;
- //
- // Control register writes.
- // Only full word writes are supported.
- //
- always @(negedge rst_n or posedge clk)
- if (~rst_n)
- begin
- irq_en <= 5'b0;
- end
- else if (valid & (&wstrb))
- case (addr)
- 2'b01: divisor <= wdata[NCO_BITS-1:0];
- 2'b11: irq_en <= wdata[status_bits-1:0];
- endcase // case (addr)
- // Read MUX
- always @(*)
- begin
- rdata = 32'b0;
- case (addr)
- 2'b01: rdata[NCO_BITS-1:0] = divisor;
- 2'b10: rdata[status_bits-1:0] = status;
- 2'b11: rdata[status_bits-1:0] = irq_en;
- default: rdata = 32'b0;
- endcase // case (addr)
- end
- // Interrupt
- always @(negedge rst_n or posedge clk)
- if (~rst_n)
- irq <= 1'b0;
- else
- irq <= |(status & irq_en);
- endmodule // tty
|