// ----------------------------------------------------------------------- // // Copyright 2004-2009 H. Peter Anvin - All Rights Reserved // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, Inc., 53 Temple Place Ste 330, // Bostom MA 02111-1307, USA; either version 2 of the License, or // (at your option) any later version; incorporated herein by reference. // // ----------------------------------------------------------------------- // // printer.v // // Simple serial "printer driver", bidirectional // // This emulates an ABC80 parallel port driver, but actually drives // a serial port. This version no longer converts FF -> FF FF and // does an automatic FF 00 on end of job; this is now done in the // option ROM for the printer port. // // - ABC-bus select code 60 decimal; // OUT 0 - (OUT) output data // OUT 4 - (C3) reset input FIFO // IN 0 - (INP) input data // INP 1 - (STAT) bit 5 - Tx busy // bit 6 - Rx available // bit 7 - flow control // module printer ( reset_n, // Global reset clk, // Clock (25 MHz) tty_txd, // Serial port data out tty_cts, // Serial port output flow control tty_rxd, // Serial port data in tty_rts, // Serial port input flow control abc_cs_n, // ABC-bus CS# abc_out_n, // ABC-bus OUT# abc_c1_n, // ABC-bus C1# abc_c2_n, // ABC-bus C2# abc_c3_n, // ABC-bus C3# abc_c4_n, // ABC-bus C4# abc_inp_n, // ABC-bus INP# abc_status_n, // ABC-bus STATUS# abc_rst_n, // ABC-bus RST# abc_do, // ABC-bus data out (cpu->card) abc_di, // ABC-bus data in (card->cpu) select // Select LED ); input reset_n; input clk; output tty_txd; input tty_rxd; output tty_rts; input tty_cts; input abc_cs_n; input abc_out_n; input abc_c1_n; input abc_c2_n; input abc_c3_n; input abc_c4_n; input abc_inp_n; input abc_status_n; input abc_rst_n; input [7:0] abc_do; output [7:0] abc_di; output select; // Which select code this device uses parameter selectcode = 6'd60; reg selected; assign select = selected; // LED // Data out reg [9:0] tx_data_sr; // Serial port shift register reg tx_data_out; // Serial port data out assign tty_txd = tx_data_out; // Baud rate divider parameter baud_rate = 115200; parameter baud_div = (25000000/baud_rate)-1; reg [7:0] baud_rate_ctr; // Time to advance the serial register wire advance_bit = ~|baud_rate_ctr; // BUSY counter reg [3:0] tx_busy_ctr; wire tx_busy = |tx_busy_ctr; // Status generation wire rx_empty; wire [7:0] status = { tty_cts, ~rx_empty, tx_busy, 5'b0 }; wire [7:0] rx_abc_data; assign abc_di = (selected & ~abc_status_n) ? status : (selected & ~abc_inp_n) ? rx_abc_data : 8'hFF; // Edge detect reg abc_out_q; reg abc_inp_q; always @(negedge reset_n or posedge clk) begin if ( ~reset_n ) begin selected <= 1'b0; baud_rate_ctr <= 0; tx_data_sr <= ~9'b0; tx_data_out <= 1'b1; tx_busy_ctr <= ~0; abc_out_q <= 1'b1; abc_inp_q <= 1'b1; end else begin if ( ~abc_rst_n ) selected <= 0; else if ( ~abc_cs_n ) selected <= (abc_do[5:0] == selectcode); if ( selected & ~abc_out_n & abc_out_q ) tx_data_sr <= { 1'b1, abc_do, 1'b0 }; else if ( advance_bit ) tx_data_sr <= { 1'b1, tx_data_sr[8:1] }; if ( advance_bit ) tx_data_out <= tx_data_sr[0]; if ( advance_bit ) baud_rate_ctr <= baud_div; else baud_rate_ctr <= baud_rate_ctr - 1; if ( selected & ~abc_out_n & abc_out_q ) tx_busy_ctr <= 4'd10; else if ( advance_bit & tx_busy ) tx_busy_ctr <= tx_busy_ctr - 1; abc_inp_q <= abc_inp_n; abc_out_q <= abc_out_n; end // else: !if( ~reset_n ) end // always @ (negedge reset_n or posedge clk) // Deglitch for the input reg [3:0] rx_deglitch; reg rx_deglitched; reg tty_rxd_q; always @(negedge reset_n or posedge clk) if ( ~reset_n ) begin rx_deglitch <= 4'b1111; tty_rxd_q <= 1'b1; end else begin tty_rxd_q <= tty_rxd; if ( tty_rxd_q ) begin if ( ~&rx_deglitch ) rx_deglitch <= rx_deglitch + 1; else rx_deglitched <= 1'b1; end else begin if ( |rx_deglitch ) rx_deglitch <= rx_deglitch - 1; else rx_deglitched <= 1'b0; end // else: !if( tty_rxd_q ) end // else: !if( ~reset_n ) // Receive logic reg [3:0] rx_bit_ctr; reg [8:0] rx_data; reg [7:0] rx_wait_ctr; reg rx_write_stb; always @(negedge reset_n or posedge clk) if (~reset_n) begin rx_bit_ctr <= 4'd0; rx_data <= 9'hxxx; rx_wait_ctr <= 9'hxxx; rx_write_stb <= 1'b0; end else begin rx_write_stb <= 1'b0; if (~|rx_bit_ctr) begin if (~rx_deglitched) begin rx_bit_ctr <= 4'd1; // Sample near the center point; half a cycle delay rx_wait_ctr <= {1'b0, baud_div[7:1]}; end end else begin if (|rx_wait_ctr) rx_wait_ctr <= rx_wait_ctr - 1; else begin rx_wait_ctr <= baud_div; rx_data <= { rx_deglitched, rx_data[8:1] }; if (rx_bit_ctr[3] & rx_bit_ctr[1]) begin // rx_data[0] and rx_deglitched are the start and // stop bits, respectively. Verify that both // were properly detected. rx_write_stb <= ~rx_data[0] & rx_deglitched; rx_bit_ctr <= 4'd0; end else rx_bit_ctr <= rx_bit_ctr + 1; end // else: !if(&rx_wait_ctr) end // else: !if(~|rx_ctr) end // else: !if(~reset_n) serrxfifo serrxfifo ( .clock (clk), .data (rx_data[7:0]), .wrreq (rx_write_stb), .sclr (~reset_n | (selected & ~abc_c3_n)), .almost_full (tty_rts), .empty (rx_empty), .full ( ), .rdreq (selected & ~abc_inp_n & abc_inp_q), .q (rx_abc_data), .usedw ( ) ); endmodule // printer