URL
https://opencores.org/ocsvn/s80186/s80186/trunk
Subversion Repositories s80186
[/] [s80186/] [trunk/] [fpga/] [ps2/] [PS2Host.sv] - Rev 2
Compare with Previous | Blame | View Log
// Copyright Jamie Iles, 2017//// This file is part of s80x86.//// s80x86 is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// s80x86 is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with s80x86. If not, see <http://www.gnu.org/licenses/>.module PS2Host #(parameter clkf=50000000)(input logic clk,input logic reset,// Host signalsoutput logic [7:0] rx,output logic rx_valid,output logic error,input logic start_tx,input logic [7:0] tx,output logic tx_busy,// Connector signalsinout ps2_clk,inout ps2_dat);localparam tx_clock_inhibit_reload = (clkf / 1000000) * 100;localparam tx_clock_inhibit_bits = $clog2(tx_clock_inhibit_reload);reg [tx_clock_inhibit_bits-1:0] clk_inhibit_count;wire ps2_clk_sync;wire ps2_dat_sync;reg last_ps2_clk;reg [2:0] bitpos;reg [7:0] tx_reg;typedef enum logic [3:0] {STATE_IDLE,// ReceiveSTATE_RX,STATE_RX_PARITY,STATE_STOP,// TransmitSTATE_INHIBIT,STATE_TX_START,STATE_TX,STATE_TX_PARITY,STATE_TX_STOP,STATE_TX_ACK} state_t;state_t state, next_state;// Host samples on the negative edge.wire do_sample = last_ps2_clk & ~ps2_clk_sync;assign rx_valid = state == STATE_STOP && do_sample;reg dat_o;reg drive_dat;assign ps2_clk = state == STATE_INHIBIT ? 1'b0 : 1'bz;assign ps2_dat = drive_dat ? dat_o : 1'bz;BitSync ClkSync(.clk(clk),.d(ps2_clk),.q(ps2_clk_sync));BitSync DatSync(.clk(clk),.d(ps2_dat),.q(ps2_dat_sync));always_ff @(posedge clk or posedge reset)if (reset)last_ps2_clk <= 1'b0;elselast_ps2_clk <= ps2_clk_sync;always_comb begincase (state)STATE_IDLE: next_state = do_sample && !ps2_dat_sync ? STATE_RX : STATE_IDLE;STATE_RX: next_state = do_sample && bitpos == 3'd7 ? STATE_RX_PARITY : STATE_RX;STATE_RX_PARITY: next_state = do_sample ? STATE_STOP : STATE_RX_PARITY;STATE_STOP: next_state = do_sample ? STATE_IDLE : STATE_STOP;STATE_INHIBIT: next_state = ~|clk_inhibit_count ? STATE_TX_START : STATE_INHIBIT;STATE_TX_START: next_state = STATE_TX;STATE_TX: next_state = do_sample && bitpos == 3'd7 ? STATE_TX_PARITY : STATE_TX;STATE_TX_PARITY: next_state = do_sample ? STATE_TX_STOP : STATE_TX_PARITY;STATE_TX_STOP: next_state = do_sample ? STATE_TX_ACK : STATE_TX_STOP;STATE_TX_ACK: next_state = do_sample ? STATE_IDLE : STATE_TX_ACK;default: next_state = STATE_IDLE;endcaseif (start_tx)next_state = STATE_INHIBIT;if (reset)next_state = STATE_IDLE;endalways_ff @(posedge clk)if (state == STATE_TX_START)dat_o <= 1'b0;else if (do_sample && state == STATE_TX)dat_o <= tx_reg[bitpos];else if (do_sample && state == STATE_TX_PARITY)dat_o <= ~^tx_reg;always_combcase (state)STATE_TX_START: drive_dat = 1'b1;STATE_TX: drive_dat = 1'b1;STATE_TX_PARITY: drive_dat = 1'b1;default: drive_dat = 1'b0;endcasealways_ff @(posedge clk or posedge reset)if (reset)tx_busy <= 1'b0;else if (start_tx)tx_busy <= 1'b1;else if (state == STATE_IDLE)tx_busy <= 1'b0;always_ff @(posedge clk)if (state == STATE_INHIBIT)clk_inhibit_count <= clk_inhibit_count - 1'b1;elseclk_inhibit_count <= tx_clock_inhibit_bits'(tx_clock_inhibit_reload);always_ff @(posedge clk)state <= next_state;always_ff @(posedge clk)if (state == STATE_RX || state == STATE_TX)bitpos <= do_sample ? bitpos + 1'b1 : bitpos;elsebitpos <= 3'b0;always_ff @(posedge clk)if (state == STATE_RX && do_sample)rx <= {ps2_dat_sync, rx[7:1]};always_ff @(posedge clk or posedge reset)if (reset)error <= 1'b0;else if (state == STATE_IDLE)error <= 1'b0;else if (state == STATE_RX_PARITY && do_sample)error <= ~^{rx, ps2_dat_sync};always_ff @(posedge clk)if (start_tx)tx_reg <= tx;endmodule
