//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //公司: //工程师: 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的感觉,所以还是很重要的^_^ //