summaryrefslogtreecommitdiffstats
path: root/keyboard.v
blob: 054780d2ac59437924c46d5f9703865d0efa9203 (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
/*
 * keyboard.v
 * 
 * Controller for a PS/2 keyboard, using a T80 CPU.
 * 
 * The Z80/T80 is quite suitable for this, as it has highly
 * predictable timings.  The frequency of the PS/2 datastream
 * is about 15 kHz, which even a slow T80 can handle with ease.
 * On the Cyclone, we should be able to get T80 up to 50 MHz if
 * we need to.
 */

module keyboard (
		 clkin,		// PLL input clock
		 reset_n,	// Reset button
		 ps2_sel,	// PS/2 port input/output select
		 ps2_kclk,	// PS/2 keyboard clock
		 ps2_kdata,	// PS/2 keyboard data
		 ps2_mclk,	// PS/2 mouse clock
		 ps2_mdata,	// PS/2 mouse data
		 led,		// LEDs
		 s7_0, s7_1,	// 7-segment displays
		 ttya_txd,	// Serial port for debugging
		 ttya_rts	// Serial port flow control
		 );

   input        clkin;
   input 	reset_n;
   output 	ps2_sel;
   inout 	ps2_kclk;
   inout 	ps2_kdata;
   inout 	ps2_mclk;
   inout        ps2_mdata;
   output [7:0] led;
   output [7:0] s7_0;
   output [7:0] s7_1;
   output 	ttya_txd;
   input 	ttya_rts;

   // Output data
   reg [7:0] 	outdata;	// Output data
   reg [7:0] 	outstat;	// Output status (e.g. meta bits)
   reg [10:0] 	tty_out;	// Output serial port debug
`define TTY_DIVISOR (50000000/115200)	// 50 MHz/115200 Hz
   reg [8:0] 	tty_div;	// Serial port clock divider
   reg [3:0] 	tty_bits;	// Number of bits in the tty shift reg
   reg [7:0] 	tty_test;
   reg [7:0] 	tty_chars;
   
   assign 	ttya_txd = tty_out[0];

   hexled hexled0 (
		   .value ( outdata[3:0] ),
		   .s7 ( s7_0[6:0] )
		   );
   hexled hexled1 (
		   .value ( outdata[7:4] ),
		   .s7 ( s7_1[6:0] )
		   );
   assign led     = outstat;
   assign s7_1[7] = ~1'b0;
   assign s7_0[7] = ~1'b0;
          
   // Output control
   reg 	  ps2_sel_q;
   reg 	  ps2_kclk_q;
   reg 	  ps2_kdata_q;
   reg 	  ps2_mclk_q;
   reg 	  ps2_mdata_q;

   assign ps2_sel   = ~ps2_sel_q;
   assign ps2_kclk  = ps2_kclk_q  ? 1'bz : 1'b0;
   assign ps2_kdata = ps2_kdata_q ? 1'bz : 1'b0;
   assign ps2_mclk  = ps2_mclk_q  ? 1'bz : 1'b0;
   assign ps2_mdata = ps2_mdata_q ? 1'bz : 1'b0;
   
   // CPU
   wire        cpu_m1_n;
   wire        cpu_iorq_n;
   wire        cpu_mreq_n;
   wire        cpu_rd_n;
   wire        cpu_wr_n;
   wire        cpu_rfsh_n;
   wire        cpu_halt_n;
   wire        cpu_busak_n;
   reg 	       cpu_wait_q;
   wire [15:0] cpu_a;
   reg [7:0]   cpu_di;
   wire [7:0]  cpu_do;
   
   T80se kbd_cpu (
		 .clk_n ( clkin ),
		 .clken ( ~cpu_wait_q ),
		 .reset_n ( reset_n ),
		 .wait_n ( 1 ),
		 .int_n ( 1 ),
		 .nmi_n ( 1 ),
		 .busrq_n ( 1 ),
		 .busak_n ( cpu_busak_n ),
		 .m1_n ( cpu_m1_n ),
		 .mreq_n ( cpu_mreq_n ),
		 .iorq_n ( cpu_iorq_n ),
		 .rd_n ( cpu_rd_n ),
		 .wr_n ( cpu_wr_n ),
		 .rfsh_n ( cpu_rfsh_n ),
		 .halt_n ( cpu_halt_n ),
		 .a ( cpu_a ),
		 .di ( cpu_di ),
		 .do ( cpu_do )
		 );

   // Memory
   wire [7:0] 	memrd;
   
   kbdram kbdram_inst (
		       .address ( cpu_a[10:0] ),
		       .clock ( ~clkin ),
		       .data ( cpu_do ),
		       .wren ( ~cpu_mreq_n & ~cpu_wr_n ),
		       .q ( memrd )
		       );
   
   // CPU input
   wire [7:0] 	ps2rd;

   assign 	ps2rd[7]   = ( tty_bits == 0 ) && ~ttya_rts;
   assign 	ps2rd[6:5] = 0;
   assign 	ps2rd[4]   = ps2_sel_q;
   assign 	ps2rd[3]   = ps2_mclk;
   assign 	ps2rd[2]   = ps2_mdata;
   assign 	ps2rd[1]   = ps2_kclk;
   assign 	ps2rd[0]   = ps2_kdata;

   assign cpu_di =
	  cpu_rd_n ? 8'bx :      // Not reading
	  ~cpu_iorq_n ? ps2rd :	 // Reading I/O
	  ~cpu_mreq_n ? memrd :	 // Reading memory
	  8'bx;			 // Reading...?

   reg 	  serial_set;

   always @( negedge reset_n or posedge clkin )
     begin
	if ( ~reset_n )
	  begin
	     ps2_sel_q   <= 0;
	     ps2_mclk_q  <= 1;
	     ps2_mdata_q <= 1;
	     ps2_kclk_q  <= 1;
	     ps2_kdata_q <= 1;

	     tty_div    <= `TTY_DIVISOR-1;
	     tty_out    <= ~0;
	     tty_bits   <= 4'd15; // Time out any (pseudo-)char being sent
	     tty_chars  <= 0;
	     
	     cpu_wait_q <= 0;
	     serial_set <= 0;
	  end
	else // clock
	  begin
	     cpu_wait_q <= 0;
	  
	     if ( tty_div == 0 )
	       begin
		  tty_div  <= `TTY_DIVISOR-1;
		  tty_out  <= { 1'b1, tty_out[10:1] };
		  tty_bits <= tty_bits ? (tty_bits-1) : 0;
	       end
	     else
	       tty_div <= tty_div-1;
	     
	     if ( ~cpu_iorq_n & ~cpu_wr_n )
	       begin
		  case ( cpu_a[1:0] )
		    2'b00:
		      begin
		       ps2_sel_q   <= cpu_do[4];
		       ps2_mclk_q  <= cpu_do[3];
		       ps2_mdata_q <= cpu_do[2];
		       ps2_kclk_q  <= cpu_do[1];
		       ps2_kdata_q <= cpu_do[0];
		      end
		    2'b01:
		      begin
			 tty_chars <= tty_chars+1;
			 tty_out[10:1] <= { 1'b1, cpu_do, 1'b0 };
			 tty_bits <= 4'd10;
			 serial_set <= 1;
		      end
		    2'b10:
		      begin
			 outdata <= cpu_do;
		      end
		    2'b11:
		      begin
			 outstat <= cpu_do;
		      end
		  endcase // case( cpu_a[1:0] )
	       end // if ( ~cpu_iorq_n & ~cpu_wr_n )
	     else
	       serial_set <= 0;

	  end // clock
     end // always
   
endmodule // keyboard