summaryrefslogtreecommitdiffstats
path: root/printer.v
blob: 79a7985b17bab1c6d358cf775c0d8eab10bb5363 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// -----------------------------------------------------------------------
//
//   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