抄来的ps2 verilog

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//公司:
//工程师: Lawson
//
//创建日期: 2004年10月10日
//设计名字:
//模块名字: PS2驱动器 Driver
//目标装置:
//工具版本: Xilinx ISE 6.3.02i
//描述:
//    按照PS2协议,驱动PS2设备并给出错误信号
//属性:
//
//修改:
//
//附加说明:
//
//
// 读时序状态图:
//
//	CLK:  
//______| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|_____…………………………______| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
//
//	延时时钟信号
//	key_clk: 
//_______| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|_____…………………………______| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
//	状态:	   Idle | Read_start | Read_start_complete|  Read_data | 
Read_data_complete |     Read_data  ←→  Read_data_complete    | 
Read_finish | Idle
//
//									   bit_0			bit_1 - bit_7,P_bit,stop_bit
//	DATA: 
// ̄ ̄ ̄ ̄ ̄ ̄ ̄|________________________________|〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓|〓〓〓〓………………………… ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
// 								       ↑		    ↑		 ↑
//	操作:							     数据读入	      counter_reg加一 数据读入
//
//
// 写时序状态图:
//		       延时100uS
//	CLK:  
//_____________________| ̄ ̄ ̄ ̄ ̄ ̄ ̄|___________________| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|______………………………………… ̄ ̄ ̄|_______________| ̄ ̄ ̄ ̄ ̄ ̄ ̄|_______________| ̄ ̄ ̄ ̄ ̄
//
//	状态: Idle | Inhibit_com ||  Write_start |     Write_data    |  
//Write_data_complete |  Write_data ←→ Write_data_complete | 	Write_stop  |  
//  Read_ACK   |    Write_finish   | Idle
//				  ↑
//         		  Inhibit_com_complete
//							   bit_0					bit_1 - bit_7,P_bit              stop_bit
//	DATA: 
// ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|________________|〓〓〓〓〓〓〓〓〓〓|〓〓〓〓〓〓〓〓〓〓〓〓〓…………………………………〓〓〓〓| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|______________________| ̄ ̄ ̄
// 				 ↑		  ↑		       ↑		    					    ↑				   ↑
//	操作:		     释放时钟线		写数据		     写数据							释放数据线		         
//读ACK
//			     下拉数据线				 counter_reg加一
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

`timescale 1 ns / 1 ns

module  PS2_Driver (
		//bidirectional
		ps2_data,					//ps2设备数据端
		ps2_clk,					//ps2设备时钟端
		//input
		w_data,						//驱动器数据输入端
		ena,						//驱动器使能端
		rw,						//驱动器读写控制端(1:读,0:写)
		drv_clk,					//驱动器时钟端
		clr_n,						//驱动器复位端
		//output
		r_data,						//驱动器数据输出端
		ready,						//驱动器完成信号
		error						//驱动器错误代码输出端
		);

//+--------------------------- 参数设置-----------------------------------+

   parameter DATA_WIDTH = 8;					//输入/输出数据宽度
   parameter COUNT	= 4;					//状态码宽度
   parameter TIME 	= 8'd100;				//抑制通信时间

//+-----------------------------------------------------------------------+

	inout ps2_data /* synthesis xc_padtype = "IOBUF_LVTTL_F_24" */;
	inout ps2_clk  /* synthesis xc_padtype = "IOBUF_LVTTL_F_24" */;

	input [DATA_WIDTH-1:0] w_data;
	input rw;
	input ena;
	input clr_n;
	input drv_clk;

	output [DATA_WIDTH-1:0] r_data;
	output ready;
	output [1:0] error;

//+-------------------------------PS2状态代码------------------------------+

	parameter Idle			= 4'h0;			//初始化

	parameter Read_start 		= 4'h1;			//读开始位
	parameter Read_start_complete 	= 4'h2;			//完成读开始位
	parameter Read_data 		= 4'h3;			//读数据
	parameter Read_data_complete 	= 4'h4;			//完成读数据
	parameter Read_finish 		= 4'h5;			//完成读操作

	parameter Inhibit_com 		= 4'h6;			//抑制通讯
	parameter Inhibit_com_complete	= 4'h7;			//完成抑制通讯
	parameter Write_start 		= 4'h8;			//写开始位
	parameter Write_data 		= 4'h9;			//写数据
	parameter Write_data_complete 	= 4'hA;			//完成写数据
	parameter Write_stop 		= 4'hB;			//写停止位
	parameter Read_ACK 		= 4'hC;			//读应答位
	parameter Write_finish 		= 4'hD;			//完成写操作

//+-----------------------------KeyBoard 错误代码--------------------------+

	parameter PARITY_ERROR 		= 2'h1;			//效验错误
	parameter NO_ACK 		= 2'h2;			//无应答错误

//+------------------------------------------------------------------------+

	reg [COUNT-1:0] current_state;
	reg [COUNT-1:0] next_state;

	reg [DATA_WIDTH:0] r_data_reg;				// |  8  |  7-0  |
								//  效验位 数据位
								// |  P  |  DATA |

	reg [DATA_WIDTH:0] w_data_reg;				// |  8  |  7-0  |
								//  效验位 数据位
								// |  P  |  DATA |
	reg clk_w;
	reg data_w;
	reg key_reg;
	reg ready;
	reg [9:0] clk_sampling_reg;
	reg [2:0] data_sampling_reg;
	reg [1:0] error;
	reg [DATA_WIDTH-1:0] counter_reg;

	wire key_data;
	wire k_data;
	wire key_clk;
	wire k_clk;
	wire clr_n;
	wire write_parity;
	wire read_parity;

//------------------------------------------------------------------------------//
//										//
//				接口逻辑函数					//
//										//
//------------------------------------------------------------------------------//

	assign r_data = r_data_reg [7:0];

	assign write_parity = ^~w_data;
	assign read_parity = ^~r_data_reg [7:0];

	//对延时4个时钟,延时7个时钟,延时10个时钟后的ps2_clk信号进行取样
	assign key_clk = (clk_sampling_reg[3]&clk_sampling_reg[6])|
	(clk_sampling_reg[6]&clk_sampling_reg[9])|(clk_sampling_reg[3]&clk_sampling_reg[9]);

	assign key_data = (data_sampling_reg[0]&data_sampling_reg[1])|
	(data_sampling_reg[1]&data_sampling_reg[2])|(data_sampling_reg[2]&data_sampling_reg[0]);

	//ps2_data双向IO口
	assign ps2_data = data_w ? 1'bz : key_reg;		// 1:输入 0:输出
	assign k_data = ps2_data;

	//ps2_data双向IO口
	assign ps2_clk = clk_w ? 1'bz : 1'b0;
	assign k_clk = ps2_clk;

//------------------------------------------------------------------------------//
//										//
//				     采样输入					//
//										//
//------------------------------------------------------------------------------//

	always @ (posedge drv_clk or negedge clr_n)
		if (!clr_n) clk_sampling_reg <= 0;
		else clk_sampling_reg <= {clk_sampling_reg[8:0],k_clk};

	always @ (posedge drv_clk or negedge clr_n)
		if (!clr_n) data_sampling_reg <= 0;
		else data_sampling_reg <= {data_sampling_reg[1:0],k_data};

//------------------------------------------------------------------------------//
//										//
//				PS2驱动器状态机					//
//										//
//------------------------------------------------------------------------------//

	always @(posedge drv_clk or negedge clr_n)
		if (!clr_n) begin
		  current_state <= Idle;
      		end
      		else begin
      		  current_state <= next_state;
		end

	always @(current_state or ena or rw or k_clk or k_data or key_clk or 
key_data or counter_reg)
		case(current_state)
		  Idle: begin
			if(ena) begin
			  if(rw) begin
			    next_state = Read_start;
			  end
			  else next_state = Inhibit_com;
			end
			else begin
			  next_state = Idle;
			end
		  end

//---------------------------------PS2驱动器读状态-------------------------------//

      		  Read_start: begin
                  	if( key_clk == 0 && key_data == 0) begin
			  next_state = Read_start_complete;
			end
			else next_state = Read_start;
               	  end

      		  Read_start_complete: begin
                  	if( key_clk == 1) begin
			  next_state = Read_data;
			end
			else next_state = Read_start_complete;
               	  end

      		  Read_data: begin														//读数据和效验位
                  	if( key_clk == 0 ) begin
			  next_state = Read_data_complete;
			end
			else next_state = Read_data;
               	  end

      		  Read_data_complete: begin
                  	if( key_clk == 1 ) begin
			  if( counter_reg == 8'd9) begin
			    next_state = Read_finish;
			  end
			  else begin
			    next_state = Read_data;
			  end
			end
			else next_state = Read_data_complete;
               	  end

		  Read_finish: begin
                  	if( key_clk == 1 && key_data == 1) begin
			  next_state = Idle;
			end
			else next_state = Read_finish;
               	  end

//---------------------------------PS2驱动器写状态-------------------------------//

      		  Inhibit_com: begin
                  	if(counter_reg == TIME) begin			//抑制通讯约100μS
			  next_state = Inhibit_com_complete;
			end
			else next_state = Inhibit_com;
               	  end

		  Inhibit_com_complete: begin
			if( k_clk == 1 ) begin
			  next_state = Write_start;
			end
			else next_state = Inhibit_com_complete;
		  end

		  Write_start: begin
			if( k_clk == 0 ) begin
			  next_state = Write_data;
			end
			  else next_state = Write_start;
               	  end

      		  Write_data: begin
                  	if( k_clk == 1 ) begin
			  next_state = Write_data_complete;
			end
			else next_state = Write_data;
               	  end

      		  Write_data_complete: begin
                  	if( k_clk == 0 ) begin
			  if( counter_reg == 8'd9) begin
			    next_state = Write_stop;
			  end
			  else begin
			   next_state = Write_data;
			  end
			end
			else next_state = Write_data_complete;
               	  end

		  Write_stop: begin
                  	if( k_clk == 1 ) begin
			  next_state = Read_ACK;
			end
			else next_state = Write_stop;
               	  end

		  Read_ACK: begin														//应答检测
                  	if( k_clk == 0) begin
			  next_state = Write_finish;
			end
			  else next_state = Read_ACK;
               	  end

		  Write_finish: begin
                  	if( k_data == 1 && k_clk == 1 ) begin
			  next_state = Idle;
			end
			else begin
			  next_state = Write_finish;
			end
               	  end

      		  default: begin
                  	next_state = 'bx;
               	  end
		endcase

//------------------------------------------------------------------------------//
//										//
//				PS2驱动器控制输出				//
//										//
//------------------------------------------------------------------------------//

   	always @( posedge drv_clk or negedge clr_n)
	  	if (!clr_n) begin
	  	  r_data_reg <= 0;
	  	  w_data_reg <= 0;
	  	  clk_w <= 0;
	  	  data_w <= 1;
	  	  key_reg <= 0;
	  	  ready <= 0;
	  	  error <= 0;
	  	  counter_reg <= 0;
      		end
      		else begin
	  	  case (current_state)

	  	  Idle: begin
			$display("Idle start");
			ready <= 0;
			clk_w <= 0;														//抑制通讯
		  end

//------------------------------------------------------------------------------//
//										//
//				PS2驱动器读操作					//
//										//
//------------------------------------------------------------------------------//

      		  Read_start: begin
		    clk_w <= 1;
		    $display("Read start");
               	  end

      		  Read_start_complete: begin
		    $display("Complete read start");
               	  end

      		  Read_data: begin
		    if( key_clk == 0 ) begin
		      r_data_reg [counter_reg] <= key_data;	//读数据、效验位和结束位
		      $display("read r_data[%d] BIT is %b",counter_reg,ps2_data);
		    end
		    else $display("Wait negative edge");
               	  end

      		  Read_data_complete: begin
		    if( key_clk == 1 ) begin
		      counter_reg <= counter_reg + 1;
		    end
		    else $display("Wait advancing edge");
               	  end

		  Read_finish: begin
		    ready <= 1;
		    counter_reg <= 0;
		    $display("PS2K parity bit is %b",r_data_reg [8]);
		    if( read_parity == r_data_reg [8] ) 
begin		//数据效验位和接收效验位比较
			$display("PS2K parity bit is right");
			$display("Complete read r_data is %b",r_data);
		    end
		    else begin
			$display("PS2K parity bit is error");
			error <= PARITY_ERROR;
		    end
               	  end

//------------------------------------------------------------------------------//
//										//
//				  PS2驱动器写操作				//
//										//
//------------------------------------------------------------------------------//

      		  Inhibit_com: begin
		    counter_reg <= counter_reg + 1;			//延时计数
		    $display("Inhibit communication");
		  end

		  Inhibit_com_complete: begin
		    if( k_clk == 1 ) begin
			w_data_reg <= {write_parity,w_data};		//写数据寄存器
			counter_reg <= 0;
		    end
		    else begin
			$display("Complete Inhibit communication");
			data_w <= 0;												//设数据线为输出
			key_reg <= 0;												//置数据线为"0"
			clk_w <= 1;													//释放时钟线
		    end
		  end

		  Write_start: begin
			if( k_clk == 0 ) begin
			  $display("write w_data_reg[%d] BIT is 
%b",counter_reg,w_data_reg[counter_reg]);
			  key_reg <= w_data_reg [counter_reg];		//写0位数据
			end
			else begin
			  $display("Write start");
			end
               	  end

      		  Write_data: begin
			if( k_clk == 1 ) begin
			  counter_reg <= counter_reg + 1;		//加位数计数器
			  key_reg <= w_data_reg [counter_reg];		//写1-7位,校验位数据
			  $display("write w_data_reg[%d] BIT is 
%b",counter_reg,w_data_reg[counter_reg]);
			end
			else begin
			  $display("Write data");
			  $display("Wait advancing edge");
			end
		  end

      		  Write_data_complete: begin
			$display("Wait negative edge");
               	  end

		  Write_stop: begin
			data_w <= 1;													//释放数据线
			counter_reg <= 0;
                  	$display("Write stop");
		  end

		  Read_ACK: begin															//应答检测
			if( k_clk == 0 ) begin
			  if( k_data == 0) begin
			    $display("PS2K_DATA ACK ");
			  end
			  else begin
			   $display("PS2K_DATA NO ACK ");
			   error <= NO_ACK;
			  end
			end
			else begin
                  	  $display("Read ACK");
			end
		  end

		  Write_finish: begin
                  	$display("Complete write data");
			ready <= 1;
		  end
   		endcase
      	end
endmodule

//1、命名规则很统一,但是不太符合惯例,惯例也就是没有人说必须,但是大家都这样做^_^,
//   一般parameter/define等用大写,其他都是小写。182-202中always @ 格式不统一。
//2、不要使用tab键或者将tab键自动转换为空格,因为不同的编辑器可能会看到不同结果,
//   就像你现在肯定不能想象我这边看到的代码是什么样子,如果使用空格就没有这个问题。
//说明一点,虽然一般代码文字编写规范不是很重要,但是好的规范会给人professional的感觉,所以还是很重要的^_^
//

 

你可能感兴趣的:(F#)