[IC design] 使用ps2 protocal的keyboard通訊協定的verilog code

本文移植於tutortecho 2019年5月15日文章

因為網路上都沒有一個真正可以動的 ps2 uart 接收鍵盤按鍵的 verilog 程式

找到後來我乾脆自己寫了一個

ps2的接頭有自己的clock rate

所以不用管 uart 通訊的 baud rate 是多少

由於 ps2_clk 的速度非常慢

所以接收 uart 的 code 不應該用高速的 system clock 來做

應該使用 ps2_clk來接收資料即可

至於接收到的資料要讓高速 clock 使用就必須使用 clock domain crossing

CDC的方式就不在這篇提了 我以前好像有寫過

module ps2_keyboard_listener (

	input rst_n,

	input ps2_clk,

	input ps2_data,  

	output reg [2:0] kb_enter,

	output reg[7:0] ps2_byte_r,

	output reg ps2_vld

);


reg[7:0] byte_tmp;

wire [7:0] rx_data;

wire data_vld;

rx urx (

	.clk(ps2_clk),

	.reset_n(rst_n),

	.din(ps2_data),

	.data(rx_data),

	.data_vld(data_vld),

	.parity_error(),

	.protocal_error()

);



always @(posedge ps2_clk or negedge rst_n) begin

	if(~rst_n) begin

		byte_tmp <= 0;

	end else if (data_vld) begin

		byte_tmp <= rx_data;

	end

end


wire kb_vld = (rx_data == 8'hf0) & data_vld;

always @(posedge ps2_clk or negedge rst_n) begin

	if(~rst_n) begin

		ps2_byte_r <= 0;

	end else if (kb_vld) begin

		ps2_byte_r <= byte_tmp;

	end

end


always @(posedge ps2_clk or negedge rst_n) begin

	if(~rst_n) begin

		ps2_vld <= 0;

	end else if (ps2_vld) begin

		ps2_vld <= 0;

	end else if (kb_vld) begin

		ps2_vld <= 1'b1;

	end

end


parameter A = 3'b001;

parameter B = 3'b010;

parameter C = 3'b011;

parameter D = 3'b100;

parameter E = 3'b101;

parameter F = 3'b110;

always@(ps2_byte_r)

begin

 case( ps2_byte_r)

   8'h1c://a

   kb_enter = A;

   8'h32://b

   kb_enter = B;

   8'h21://c

   kb_enter = C;

   8'h23://d 

   kb_enter = D;

   8'h24://e

   kb_enter = E;

   8'h2b://f

   kb_enter = F;

   default:

   kb_enter = 3'b000;

   endcase

end


endmodule


module rx(

	input clk,

	input reset_n,

	input din,

	output reg [7:0]data,

	output data_vld,

	output reg parity_error,

	output reg protocal_error

);


	reg [3:0]count;

	wire [3:0] count_nx;

	wire start;

	wire parity;

	wire stop;

	wire [7:0]data_nx;

	wire parity_error_nx;

	wire protocal_error_nx;



	assign start = (count == 4'd0) & (~din);

	assign tras = (count > 4'd0) & (count < 4'd9);

	assign parity = count == 4'd9;

	assign stop = count == 4'd10;

	assign count_nx = (start) ? 4'd1 :

					  (tras | parity) ? (count + 4'd1) :

					  (stop) ? 4'd0 : count;


	assign data_nx = (tras) ? {din, data[7:1]}: data;

	assign parity_error_nx = (parity) ? ^data: 1'd0;

	assign protocal_error_nx = (stop & (~din));


	always @(posedge clk or negedge reset_n) begin 

		if(~reset_n) begin

			data <= 8'd0;

			count <= 4'd0;

		end else begin

			data <= data_nx;

			count <= count_nx;

		end

	end




	always @(posedge clk or negedge reset_n) begin 

		if(~reset_n) begin

			parity_error <= 1'd0;

		end else if(~parity_error) begin

			parity_error <= parity_error_nx;

		end

	end




	always @(posedge clk or negedge reset_n) begin 

		if(~reset_n) begin

			protocal_error <= 1'd0;

		end else if(~protocal_error) begin

			protocal_error <= protocal_error_nx;

		end

	end


	assign data_vld = count == 4'd9;

endmodule : rx


發表留言