ps2_keyboard_tdv¶
Source: Verilog/Terminals/rtl/ps2_keyboard_tdv.v
Where it sits (Nexys): nd120_nexys4ddr_top > ps2_keyboard_tdv
- instance path: KEYBOARD
Used in: nd120_nexys4ddr_top (Nexys)
Contains: ps2_decoder_tdv
Module hierarchy - All modules

Schematic¶
Drawn from the Verilog: the yosys netlist of the Nexys 4 DDR build, instance KEYBOARD. Sub-modules are boxes (click the picture to open it full size; there every sub-module box links to its page, and every wire shows its Verilog name).
Parameters¶
| Parameter | Default |
|---|---|
FILTER_LEN |
8 |
Ports¶
| Direction | Width | Name | Description |
|---|---|---|---|
| input | 1 |
clk |
|
| input | 1 |
rst_n (active low) |
|
| input | 1 |
ps2_clk_in |
|
| input | 1 |
ps2_data_in |
|
| input | 1 |
layout_no |
|
| output | 1 |
ascii_valid |
|
| output | [7:0] |
ascii_data |
|
| output | 1 |
code_valid |
|
| output | [7:0] |
code_data |
|
| output | 1 |
code_release |
|
| output | 1 |
code_extended |
Verilog source¶
Verilog/Terminals/rtl/ps2_keyboard_tdv.v on GitHub.
Show the Verilog of ps2_keyboard_tdv (177 lines)
//============================================================================
//! PS/2 keyboard receiver + scancode-set-2 to TDV2200 decoder
//!
//! Part of the board-independent terminal core (Verilog/Terminals/).
//! Thin sibling of ps2_keyboard.v (VT100) - the 11-bit PS/2 framing and
//! E0/F0 prefix handling are IDENTICAL (that part is board wiring, not
//! terminal-type logic); the only difference is instantiating
//! ps2_decoder_tdv instead of ps2_decoder, per the decision to keep VT100
//! and TDV2200 as two separate, compile-time-selected modules.
//!
//! Written 31-AUG-2026.
//============================================================================
`default_nettype none
module ps2_keyboard_tdv #(
parameter integer FILTER_LEN = 8
) (
input wire clk,
input wire rst_n,
input wire ps2_clk_in,
input wire ps2_data_in,
input wire layout_no,
output wire ascii_valid,
output wire [7:0] ascii_data,
output reg code_valid,
output reg [7:0] code_data,
output reg code_release,
output reg code_extended
);
//--------------------------------------------------------------------------
// Input conditioning: synchronize, then require FILTER_LEN stable samples
//--------------------------------------------------------------------------
reg [1:0] s_clk_sync;
reg [1:0] s_dat_sync;
reg [FILTER_LEN-1:0] s_clk_filter;
reg s_clk_stable;
reg s_clk_stable_d;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
s_clk_sync <= 2'b11;
s_dat_sync <= 2'b11;
s_clk_filter <= {FILTER_LEN{1'b1}};
s_clk_stable <= 1'b1;
s_clk_stable_d <= 1'b1;
end else begin
s_clk_sync <= {s_clk_sync[0], ps2_clk_in};
s_dat_sync <= {s_dat_sync[0], ps2_data_in};
s_clk_filter <= {s_clk_filter[FILTER_LEN-2:0], s_clk_sync[1]};
if (s_clk_filter == {FILTER_LEN{1'b1}}) s_clk_stable <= 1'b1;
else if (s_clk_filter == {FILTER_LEN{1'b0}}) s_clk_stable <= 1'b0;
s_clk_stable_d <= s_clk_stable;
end
end
wire s_falling_edge = s_clk_stable_d && !s_clk_stable;
//--------------------------------------------------------------------------
// Frame receiver - 11 bits, LSB first
//--------------------------------------------------------------------------
reg [3:0] s_bit_count;
reg [10:0] s_shift;
wire [10:0] s_frame_next = {s_dat_sync[1], s_shift[10:1]};
wire s_frame_start = s_frame_next[0];
wire s_frame_stop = s_frame_next[10];
wire s_parity_ok = (^s_frame_next[9:1]) == 1'b1;
wire s_frame_ok = (s_frame_start == 1'b0) && (s_frame_stop == 1'b1) && s_parity_ok;
reg s_byte_valid;
reg [7:0] s_byte_data;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
s_bit_count <= 4'd0;
s_shift <= 11'd0;
s_byte_valid <= 1'b0;
s_byte_data <= 8'h00;
end else begin
s_byte_valid <= 1'b0;
if (s_falling_edge) begin
s_shift <= s_frame_next;
if (s_bit_count == 4'd10) begin
s_bit_count <= 4'd0;
if (s_frame_ok) begin
s_byte_valid <= 1'b1;
s_byte_data <= s_frame_next[8:1];
end
end else begin
s_bit_count <= s_bit_count + 4'd1;
end
end
end
end
//--------------------------------------------------------------------------
// Prefix handling - E0 (extended) and F0 (release)
//--------------------------------------------------------------------------
localparam [7:0] SC_RELEASE = 8'hF0;
localparam [7:0] SC_EXTENDED = 8'hE0;
reg s_pending_release;
reg s_pending_extended;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
s_pending_release <= 1'b0;
s_pending_extended <= 1'b0;
code_valid <= 1'b0;
code_data <= 8'h00;
code_release <= 1'b0;
code_extended <= 1'b0;
end else begin
code_valid <= 1'b0;
if (s_byte_valid) begin
case (s_byte_data)
SC_RELEASE: s_pending_release <= 1'b1;
SC_EXTENDED: s_pending_extended <= 1'b1;
default: begin
code_valid <= 1'b1;
code_data <= s_byte_data;
code_release <= s_pending_release;
code_extended <= s_pending_extended;
s_pending_release <= 1'b0;
s_pending_extended <= 1'b0;
end
endcase
end
end
end
//--------------------------------------------------------------------------
// The TDV2200 decoder
//--------------------------------------------------------------------------
ps2_decoder_tdv DECODER (
.clk (clk),
.rst_n(rst_n),
.code_valid (code_valid),
.code_data (code_data),
.code_release (code_release),
.code_extended(code_extended),
.layout_no(layout_no),
.ascii_valid(ascii_valid),
.ascii_data (ascii_data),
.shift_active(),
.ctrl_active (),
.caps_active (),
.alt_active ()
);
endmodule
`default_nettype wire