vt52-fpga  1.0.0 Initial
vt52-fpga is a serial terminal implemented on a FPGA
keyboard.v
Go to the documentation of this file.
1 module keyboard
2  (input clk,
3  input reset,
4  input ps2_data,
5  input ps2_clk,
6  output reg [7:0] data,
7  output reg valid,
8  input ready
9  );
10 
11  // state: one hot encoding
12  // idle is the normal state, reading the ps/2 bus
13  // key up/down (long and short) are for key events
14  // keymap_read is for reading the keymap rom
15  // esc_char is for sending ESC- prefixed chars
16  localparam state_idle = 5'b00001;
17  localparam state_keymap = 5'b00010;
18  localparam state_key_down = 5'b00100;
19  localparam state_key_up = 5'b01000;
20  localparam state_esc_char = 5'b10000;
21 
22  localparam esc = 8'h1b;
23 
24  localparam keycode_regular = 2'b0x; // djrm, doent work as block design module, use 2'b00 and 2'b01
25 // localparam keycode_regular = 2'b00;
26 // localparam keycode_regular_ = 2'b01;
27  localparam keycode_modifier = 2'b10;
28  localparam keycode_escaped = 2'b11;
29 
30  reg [4:0] state;
31 
32  reg [1:0] ps2_old_clks;
33  reg [10:0] ps2_raw_data;
34  reg [3:0] ps2_count;
35  reg [7:0] ps2_byte;
36  // we are processing a break_code (key up)
37  reg ps2_break_keycode;
38  // we are processing a long keycode (two bytes)
39  reg ps2_long_keycode;
40  // shift, control & meta key status, bit order:
41  // lshift, lcontrol, lmeta, rmeta, rcontrol, rshift
42  // alt/meta key status, vt52 doesn't have meta, but I want to use
43  // emacs & can't stand that Esc- business, so alt sends esc+keypress
44  reg [5:0] modifier_pressed;
45  wire shift_pressed = modifier_pressed[5] || modifier_pressed[0];
46  wire control_pressed = modifier_pressed[4] || modifier_pressed[1];
47  wire meta_pressed = modifier_pressed[3] || modifier_pressed[2];
48  // caps lock
49  reg caps_lock_active;
50  // keymap
51  wire [10:0] keymap_address;
52  wire [7:0] keymap_data;
53  // special char to send after ESC
54  reg [7:0] special_data;
55 
56  // ps2_byte is the actual keycode, we use long/short keycode, caps lock &
57  // shift to determine the plane we need
58  assign keymap_address = { ps2_long_keycode, caps_lock_active, shift_pressed, ps2_byte };
59 
60  // address is 3 bits for longkeycode/capslock/shift + 8 bits for keycode
61  // data is on of
62  // 0xxxxxxx: regular ASCII key
63  // 10xxxxxx: control/meta/shift or caps lock, each bit is a key, all 0 for caps lock
64  // 11xxxxxx: special key, ESC + upper case ASCII (clear msb to get char)
65  keymap_rom keymap_rom(.clk(clk),
66  .addr(keymap_address),
67  .dout_(keymap_data)
68  );
69 
70  // we don't need to do this on the pixel clock, we could use
71  // something way slower, but it works
72  always @(posedge clk) begin
73  if (reset) begin
74  state <= state_idle;
75 
76  data <= 0;
77  valid <= 0;
78 
79  // the clk is usually high and pulled down to start
80  ps2_old_clks <= 2'b00;
81  ps2_raw_data <= 0;
82  ps2_count <= 0;
83  ps2_byte <= 0;
84 
85  ps2_break_keycode <= 0;
86  ps2_long_keycode <= 0;
87 
88  modifier_pressed = 6'h00;
89  caps_lock_active <= 0;
90 
91  special_data <= 0;
92  end
93  else if (valid && ready) begin
94  // as soon as data is transmitted, clear valid
95  valid <= 0;
96  end
97  else begin
98  case (state)
99  state_idle: begin
100  ps2_old_clks <= {ps2_old_clks[0], ps2_clk};
101  if(ps2_clk && ps2_old_clks == 2'b01) begin
102  // clock edge detected, read another bit
103  if(ps2_count == 10) begin
104  // 11 bits means we are done (XXX/TODO check parity and stop bits)
105  ps2_count <= 0;
106  ps2_byte <= ps2_raw_data[10:3];
107  // handle the breaks & long keycodes and only change to
108  // keycode state if a complete keycode is already received
109  if (ps2_raw_data[10:3] == 8'he0) begin
110  ps2_break_keycode <= 0;
111  ps2_long_keycode <= 1;
112  end
113  else if (ps2_raw_data[10:3] == 8'hf0) begin
114  ps2_break_keycode <= 1;
115  end
116  else begin
117  state <= state_keymap;
118  end
119  end
120  else begin
121  // the data comes lsb first
122  ps2_raw_data <= {ps2_data, ps2_raw_data[10:1]};
123  ps2_count <= ps2_count + 1;
124  end
125  end
126  end
127  state_keymap: begin
128  // after reading the keymap we can finally process the key
129  state <= ps2_break_keycode? state_key_up : state_key_down;
130  end
131  state_key_up: begin
132  // on key up we only care about released modifiers
133  ps2_break_keycode <= 0;
134  ps2_long_keycode <= 0;
135  state <= state_idle;
136  if (keymap_data[7:6] == keycode_modifier) begin
137  // the released modifier is in keymap_data[5:0]
138  // or 0 for caps lock
139  modifier_pressed <= modifier_pressed & ~keymap_data[5:0];
140  end
141  end
142  state_key_down: begin
143  ps2_long_keycode <= 0;
144  if (keymap_data == 0) begin
145  // unrecognized key, just go back to idle
146  state <= state_idle;
147  end
148  else begin
149  casex (keymap_data[7:6])
150  keycode_regular: begin
151  // regular key, apply modifiers:
152  // control turns off 7th & 6th bits
153  // meta sends an ESC prefix
154  if (meta_pressed) begin
155  data <= esc;
156  valid <= 1;
157  state <= state_esc_char;
158  special_data <= {
159  1'b0,
160  control_pressed? 2'b00 : keymap_data[6:5],
161  keymap_data[4:0]
162  };
163  end
164  else begin
165  data <= {
166  1'b0,
167  control_pressed? 2'b00 : keymap_data[6:5],
168  keymap_data[4:0]
169  };
170  valid <= 1;
171  state <= state_idle;
172  end
173  end
174  keycode_escaped: begin
175  // escaped char, send Esc- and then the ascii value
176  // including the leading 1 (only uppercase and some symbols)
177  data <= esc;
178  valid <= 1;
179  state <= state_esc_char;
180  special_data <= {
181  1'b0,
182  control_pressed? 2'b00 : keymap_data[6:5],
183  keymap_data[4:0]
184  };
185  end
186  keycode_modifier: begin
187  // the pressed modifier is in keymap_data[5:0], or 0 for caps lock
188  state <= state_idle;
189  modifier_pressed <= modifier_pressed | keymap_data[5:0];
190  caps_lock_active <= caps_lock_active ^ ~|keymap_data[5:0];
191  end
192  endcase
193  end // else: !if(keymap_data == 0)
194  end // case: state_keymap_down
195  state_esc_char: begin
196  // only send special char after ESC was successfully sent
197  if (valid == 0) begin
198  state <= state_idle;
199  data <= {
200  1'b0,
201  control_pressed? 2'b00 : special_data[6:5],
202  special_data[4:0]
203  };
204  valid <= 1;
205  end
206  end
207  endcase // case (state)
208  end // else: !if(valid && ready)
209  end // always @ (posedge clk)
210 endmodule
module keymap_rom(input clk, input[10:0] addr, output[8] dout_)
Keymap ROM (2KB, maps keycodes to ASCII chars) This could be a RAM to allow keymap modifications,...
Definition: keymap_rom.v:12
module keyboard(input clk, input reset, input ps2_data, input ps2_clk, output reg< 7:0 > data, output reg valid, input ready)
Definition: keyboard.v:2
always(posedge clk)
Definition: uart_rx.v:86