Hi,
I’m in need of some sanity check on my myHDL code. I’ve written a PS/2 interface with the intent on emulating a Keyboard on an FPGA.
Bare in mind I am a novice in HDL and FPGA, playing around as a hobby.
My testbench are behaving as expected, but when I put it on the FPGA, I have some trouble.
My ps2 core is the following (hopefully this is easy to read):
from myhdl import block, always, always_seq, modbv, intbv, enum, Signal, now
@block
def ps2_ctrl(clock, ps2_clk, ps2_dat, data_in, data_out, send_data, data_sent, data_received, parity_error):
t_state = enum('IDLE', 'DTH_START', 'DTH_BYTE', 'DTH_PARITY', 'DTH_STOP', 'DTH_SENT', 'HTD_START', 'HTD_BYTE', 'HTD_PARITY', 'ACK', 'HTD_END')
t_clock_state = enum('RISE', 'HIGH', 'FALL', 'LOW')
state = Signal(t_state.IDLE)
clock_state = Signal(t_clock_state.LOW)
parity = Signal(bool(0))
bit_idx = Signal(intbv(val=0, min=0, max=8))
wait_interupt = Signal(intbv(val=0, min=0, max=2**5))
ps2_clk_d = ps2_clk.driver()
ps2_dat_d = ps2_dat.driver()
@always(clock.posedge)
def proc():
if state != t_state.IDLE:
# Generate ps/2 clock when not idling
if clock_state == t_clock_state.HIGH:
ps2_clk_d.next = None
clock_state.next = t_clock_state.FALL
elif clock_state == t_clock_state.FALL:
ps2_clk_d.next = False
clock_state.next = t_clock_state.LOW
elif clock_state == t_clock_state.LOW:
ps2_clk_d.next = False
clock_state.next = t_clock_state.RISE
elif clock_state == t_clock_state.RISE:
ps2_clk_d.next = None
clock_state.next = t_clock_state.HIGH
# Host request the line to send data
if clock_state == t_clock_state.HIGH and ps2_clk == False:
ps2_clk_d.next = None
ps2_dat_d.next = None
state.next = t_state.HTD_START
# idle state if nothing is happening
elif state == t_state.IDLE:
ps2_clk_d.next = None
ps2_dat_d.next = None
data_received.next = 0
data_sent.next = 0
wait_interupt.next = 0
clock_state.next = t_clock_state.HIGH
parity_error.next = 0
if send_data == 1:
# Stop idling and start sending a message
parity.next = 1
data_sent.next = 0
data_received.next = 0
data_out.next = 0x00
state.next = t_state.DTH_START
# *********************************************************************
# ********************* Device To Host ********************************
# *********************************************************************
# IDLE => DTH_START (bit 0) => DTH_BYTE (8bit of data_in) => DTH_PARITY (parity bit) => DTH_STOP (bit 1) => DTH_SENT (signaling) => IDLE
elif state == t_state.DTH_START:
if clock_state == t_clock_state.HIGH:
ps2_dat_d.next = False
bit_idx.next = 0
state.next = t_state.DTH_BYTE
elif state == t_state.DTH_BYTE:
if clock_state == t_clock_state.HIGH:
ps2_dat_d.next = False if not data_in[bit_idx] else None
parity.next = not parity and data_in[bit_idx]
if bit_idx == 7:
state.next = t_state.DTH_PARITY
else:
bit_idx.next = bit_idx + 1
elif state == t_state.DTH_PARITY:
if clock_state == t_clock_state.HIGH:
ps2_dat_d.next = False if not parity else None
state.next = t_state.DTH_STOP
elif state == t_state.DTH_STOP:
if clock_state == t_clock_state.HIGH:
ps2_dat_d.next = None
state.next = t_state.DTH_SENT
elif state == t_state.DTH_SENT:
if clock_state == t_clock_state.HIGH:
data_sent.next = 1
ps2_dat_d.next = None
clock_state.next = t_clock_state.HIGH
if send_data == 0:
state.next = t_state.IDLE
# *********************************************************************
# ********************** Host to Device *******************************
# *********************************************************************
# ANY => HTD_START (receive bit 0) => HTD_BYTE (8 bit goes to data_out) => HTD_PARITY (check parity error) => ACK => HTD_END => IDLE
elif state == t_state.HTD_START:
ps2_clk_d.next = None
ps2_dat_d.next = None
clock_state.next = t_clock_state.RISE
if ps2_dat == False:
state.next = t_state.HTD_BYTE
bit_idx.next = 0
clock_state.next = t_clock_state.HIGH
else:
if wait_interupt == 31:
state.next = t_state.IDLE
else:
wait_interupt.next = wait_interupt + 1
elif state == t_state.HTD_BYTE:
if clock_state == t_clock_state.HIGH:
data_out.next[bit_idx] = bool(ps2_dat == None)
parity.next = not parity and bool(ps2_dat == None)
if bit_idx == 7:
state.next = t_state.HTD_PARITY
else:
bit_idx.next = bit_idx + 1
elif state == t_state.HTD_PARITY:
if clock_state == t_clock_state.HIGH:
if parity != ps2_dat:
parity_error.next = 1
state.next = t_state.ACK
elif state == t_state.ACK:
if clock_state == t_clock_state.HIGH:
ps2_dat_d.next = False
data_received.next = 1
if data_received == 1:
data_received.next = 0
state.next = t_state.HTD_END
elif state == t_state.HTD_END:
if clock_state == t_clock_state.HIGH:
state.next = t_state.IDLE
return proc
I instanciate it like this:
# Actual FPGA pin
clock = Signal(bool(0)) # 50 MHz
ps2_clk = TristateSignal(bool(0))
ps2_dat = TristateSignal(bool(0))
# inside signals
ps2_data_in = Signal(intbv(val=0, min=0, max=2**8))
ps2_data_out = Signal(intbv(val=0, min=0, max=2**8))
send_data = Signal(bool(0))
data_sent = Signal(bool(0))
data_received = Signal(bool(0))
parity_error = Signal(bool(0))
clock_i = Signal(bool(0))
clock_i_driver = clock_reducer(clock, clock_i, 1000) # 50 MHz clock reduced to 50 kHz
ps2_interface = ps2_ctrl(clock_i, ps2_clk, ps2_dat, ps2_data_in, ps2_data_out, send_data, data_sent, data_received, parity_error)
#########
# .....Code elipsis to avoid cluttering the post....
#########
I’m basing the implementation on multiple source but in particular this one: Wayback Machine
I have additional code to drive it and get output back
I convert the myHDL code to VHDL and then synthesize to a DE0-nano (cyclone IV) through JTAG
I plug my gpios into a ‘Ps/2 to USB adapter’, my understanding is that there is a ps/2 controller inside the adapter (I don’t know which one)
I manage to send data, correct key presses are registered on the pc but are spammed forever (even though I send them only once followed by the break codes)
When I receive data from the adapter, it’s always 0x00, sometimes there are parity error in the message. This led me to believe I must do something wrong in my host-to-device implementation.
Any idea what I did wrong ? What can I do to further test/fix this ?