logic-circuit-design / practice / 05_PS2_Keybord / rtl / PS2.v
PS2.v
Raw
//
// Permission:
//
//   Terasic grants permission to use and modify this code for use
//   in synthesis for all Terasic Development Boards and Altera Development 
//   Kits made by Terasic.  Other use of this code, including the selling 
//   ,duplication, or modification of any portion is strictly prohibited.
//
// Disclaimer:
//
//   This VHDL/Verilog or C/C++ source code is intended as a design reference
//   which illustrates how these types of functions can be implemented.
//   It is the user's responsibility to verify their design for
//   consistency and functionality through the use of formal
//   verification methods.  Terasic provides no warranty regarding the use 
//   or functionality of this code.
//
// --------------------------------------------------------------------
//           
//                     Terasic Technologies Inc
//                     356 Fu-Shin E. Rd Sec. 1. JhuBei City,
//                     HsinChu County, Taiwan
//                     302
//
//                     web: http://www.terasic.com/
//                     email: support@terasic.com
//
// --------------------------------------------------------------------
//
// Major Functions:	DE2_115_PS2 Mouse Controller 
//
// --------------------------------------------------------------------
//
// Revision History :
// --------------------------------------------------------------------
//   Ver  :| Author                    :| Mod. Date :| Changes Made:
//   V1.0 :| Johnny FAN,HdHuang        :| 05/16/10  :| Initial Revision
//   V1.1 :| Koh Johguchi              :| 10/30/17  :| Shinshu Univ. 
//        :|   -  Devided clk. 
// --------------------------------------------------------------------
module PS2(
           iSTART,    //press the button for transmitting instrucions to device;
           iRST_n,    //FSM reset signal;
           iCLK,      //clock source;
           PS2_CLK,   //ps2_clock signal inout;
           PS2_DAT,   //ps2_data  signal inout;
           oLEFBUT,   //left button press display;
           oRIGBUT,   //right button press display;
           oMIDBUT,   //middle button press display;
			  PS2_READY, //Ready signal for PS2;
           SCANCODE   //scan-code;
           ); 
           //interface;
//=======================================================
//  PORT declarations
//=======================================================

input iSTART;
input iRST_n;
input iCLK;

inout PS2_CLK;
inout PS2_DAT;

output oLEFBUT;
output oRIGBUT;
output oMIDBUT;
output PS2_READY;
output [15:0] SCANCODE;

//instruction define, users can charge the instruction byte here for other purpose according to ps/2 mouse datasheet.
//the MSB is of parity check bit, that's when there are odd number of 1's with data bits, it's value is '0',otherwise it's '1' instead.

parameter enable_byte =9'b011110100;


//=======================================================
//  REG/WIRE declarations
//=======================================================
reg [1:0]  cur_state,nex_state;
reg        ce,de;
reg [3:0]  byte_cnt,delay;
reg [5:0]  ct;
reg [15:0] scode;
reg [7:0]  cnt;
reg [9:0]  clk_div;
reg [9:0]  dout_reg;
reg [32:0] shift_reg;
reg        leflatch,riglatch,midlatch;
reg        ps2_clk_in,ps2_clk_syn1,ps2_dat_in,ps2_dat_syn1;
reg        ready;
wire       clk,ps2_dat_syn0,ps2_clk_syn0,ps2_dat_out,ps2_clk_out,flag;

//=======================================================
//  PARAMETER declarations
//=======================================================
//state define
parameter listen =2'b00,
          pullclk=2'b01,
          pulldat=2'b10,
          trans  =2'b11;
	
assign clk = iCLK;

//tristate output control for PS2_DAT and PS2_CLK;
assign PS2_CLK = ce?ps2_clk_out:1'bZ;
assign PS2_DAT = de?ps2_dat_out:1'bZ;
assign ps2_clk_out = 1'b0;
assign ps2_dat_out = dout_reg[0];
assign ps2_clk_syn0 = ce?1'b1:PS2_CLK;
assign ps2_dat_syn0 = de?1'b1:PS2_DAT;
//
assign oLEFBUT   = leflatch;
assign oRIGBUT   = riglatch;
assign oMIDBUT   = midlatch;
assign SCANCODE  = scode;
assign PS2_READY = ready;
//multi-clock region simple synchronization
always@(posedge clk)
	begin
		ps2_clk_syn1 <= ps2_clk_syn0;
		ps2_clk_in   <= ps2_clk_syn1;
		ps2_dat_syn1 <= ps2_dat_syn0;
		ps2_dat_in   <= ps2_dat_syn1;
	end

//FSM shift
always@(posedge clk)
begin
   case(cur_state)
     listen  :begin
              if ((!iSTART) && (cnt == 8'b11111111)) 
              begin
                 nex_state <= pullclk;
                        ce <= ce;
                        de <= de;
              end else begin
                 nex_state <= listen;
                        ce <= 1'b0;
                        de <= 1'b0;
              end end
     pullclk :begin
              if (delay == 4'b1100)
              begin
                 nex_state <= pulldat;
                        ce <= ce;
                        de <= de;
              end else begin
                 nex_state <= pullclk;
                        ce <= 1'b1;
                        de <= 1'b0;
              end end
     pulldat :begin
                 nex_state <= trans;
                        ce <= 1'b1;
                        de <= 1'b1;
              end
     trans   :begin
              if  (byte_cnt == 4'b1010)
              begin
                 nex_state <= listen;
                        ce <= ce;
                        de <= de;
              end else begin    
                 nex_state <= trans;
                        ce <= 1'b0;
                        de <= 1'b1;
              end end
     default :begin
                 nex_state <= listen;
                        ce <= ce;
                        de <= de;
              end
   endcase
end

//idle counter
always@(posedge clk)
begin
   if ({ps2_clk_in,ps2_dat_in} == 2'b11)
   begin
     cnt <= cnt+1;
   end else begin
     cnt <= 8'd0;
   end
end

//periodically reset ct; ct counts the received data length;
assign flag = (cnt == 8'hff)?1:0;
always@(posedge ps2_clk_in or posedge flag)
begin
   if (flag)
      ct <= 6'b000000;
   else
      ct <= ct+1;
end
//latch data from shift_reg;outputs is of 2's complement;
//Please treat the cnt value here with caution, otherwise wrong data will be latched.
always@(posedge clk or negedge iRST_n)
begin
   if (!iRST_n) 
   begin
      leflatch <= 1'b0;
      riglatch <= 1'b0;
      midlatch <= 1'b0;
      scode    <= 24'd0;
      ready    <= 1'b1;
   end
   else if (cnt == 8'b00011110 && (ct[5] == 1'b1 || ct[4] == 1'b1))
   begin
      leflatch <= shift_reg[1];
      riglatch <= shift_reg[2];
      midlatch <= shift_reg[3];
      scode    <= {shift_reg[19:12],shift_reg[30:23]};
      ready    <= 1'b0;
   end
   else
   begin
      leflatch <= leflatch;
      riglatch <= riglatch;
      midlatch <= midlatch;
      scode    <= scode;
      ready    <= 1'b1;
   end
end

//pull ps2_clk low for 100us before transmit starts;
always@(posedge clk)
begin
  if (cur_state == pullclk)
     delay <= delay+1;
  else
     delay <= 4'b0000;
end
//transmit data to ps2 device;eg. 0xF4
always@(negedge ps2_clk_in)
begin
  if (cur_state == trans)
     dout_reg <= {1'b0,dout_reg[9:1]};
  else
     dout_reg <= {enable_byte,1'b0};
end
//transmit byte length counter
always@(negedge ps2_clk_in)
begin
  if (cur_state == trans)
     byte_cnt <= byte_cnt+1;
  else
     byte_cnt <= 4'b0000;
end
//receive data from ps2 device;
always@(negedge ps2_clk_in)
begin
  if (cur_state == listen)
     shift_reg <= {ps2_dat_in,shift_reg[32:1]};
end

//FSM movement
always@(posedge clk or negedge iRST_n)
begin
  if (!iRST_n)
     cur_state <= listen;
  else
     cur_state <= nex_state;
end
endmodule