summaryrefslogtreecommitdiffstats
path: root/keyboard.v
blob: 17146c28d580910dc86a572603aca65b2f0bcba1 (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
/*
 * 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_kclk,	// PS/2 keyboard clock
		 ps2_kdata,	// PS/2 keyboard data
		 ps2_mclk,	// PS/2 mouse clock
		 ps2_mdata,	// PS/2 mouse data
		 kb_data,	// Output character code
		 kb_stat	// Output status word
		 );

   input        clkin;
   input 	reset_n;
   inout 	ps2_kclk;
   inout 	ps2_kdata;
   inout 	ps2_mclk;
   inout        ps2_mdata;
   output [7:0] kb_data;
   output [7:0] kb_stat;
   
   // Output data
   reg [7:0] 	outdata;	// Output data
   reg [7:0] 	outstat;	// Output status (e.g. meta bits)

   assign 	kb_data = outdata;
   assign 	kb_stat = outstat;
             
   // Output control: all outputs are open drain
   reg 	  ps2_kclk_q;
   reg 	  ps2_kdata_q;
   reg 	  ps2_mclk_q;
   reg 	  ps2_mdata_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_reti_n;
   wire [15:0] cpu_a;
   wire [7:0]  cpu_di;
   wire [7:0]  cpu_do;
   
   T80s kbd_cpu (
		 .clk_n ( clkin ),
		 .reset_n ( reset_n ),
		 .wait_n ( 1 ),
		 .int_n ( 1 ),
		 .nmi_n ( 1 ),
		 .busrq_n ( 1 ),
		 .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 ),
		 .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:4] = 3'b1000; // Bit 7 is used for debugging
   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'hFF;		 // Reading nothing

   always @( negedge reset_n or posedge clkin )
     begin
	if ( ~reset_n )
	  begin
	     ps2_mclk_q  <= 1'b1;
	     ps2_mdata_q <= 1'b1;
	     ps2_kclk_q  <= 1'b1;
	     ps2_kdata_q <= 1'b1;
	     outdata     <= 8'h00;
	     outstat     <= 8'h00;
	  end
	else // clock
	  begin
	     if ( ~cpu_iorq_n & ~cpu_wr_n )
	       begin
		  case ( cpu_a[1:0] )
		    2'b00:
		      begin
		       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
			 /* Used for debugging only */
		      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 )
	  end // clock
     end // always
   
endmodule // keyboard