3535# The Intel 8042 controller fixes, or rather works around, this problem by completely encapsulating
3636# the handling of PS/2. In fact many (if not most) PS/2 commands that are sent to i8042 result in
3737# no PS/2 traffic at all, with their response being either synthesized on the fly on the i8042, or,
38- # even worse, them being an in-band commands to i8042 itself. This lead mice developers to a quite
38+ # even worse, them being in-band commands to i8042 itself. This lead mice developers to a quite
3939# horrifying response: any advanced settings are conveyed to the mouse and back by abusing
4040# the sensitivity adjustment commands as 2-bit at a time a communication channel; keyboards use
4141# similar hacks.
6161
6262import logging
6363import asyncio
64+
6465from amaranth import *
65- from amaranth .lib import data , io
66- from amaranth .lib .cdc import FFSynchronizer
66+ from amaranth .lib import data , io , wiring , stream , cdc
67+ from amaranth .lib .wiring import In , Out
68+
69+ from glasgow .applet import GlasgowAppletV2 , GlasgowAppletError
6770
68- from ... import *
71+
72+ __all__ = ["PS2HostComponent" , "PS2HostInterface" , "PS2HostError" ]
6973
7074
7175_frame_layout = data .StructLayout ({
@@ -91,16 +95,18 @@ def _prepare_frame(frame, data):
9195 ]
9296
9397
94- class PS2Bus (Elaboratable ):
98+ class PS2Bus (wiring .Component ):
99+ falling : Out (1 )
100+ rising : Out (1 )
101+ clock_i : Out (1 , init = 1 )
102+ clock_o : In (1 , init = 1 )
103+ data_i : Out (1 , init = 1 )
104+ data_o : In (1 , init = 1 )
105+
95106 def __init__ (self , ports ):
96107 self .ports = ports
97108
98- self .falling = Signal ()
99- self .rising = Signal ()
100- self .clock_i = Signal (init = 1 )
101- self .clock_o = Signal (init = 1 )
102- self .data_i = Signal (init = 1 )
103- self .data_o = Signal (init = 1 )
109+ super ().__init__ ()
104110
105111 def elaborate (self , platform ):
106112 m = Module ()
@@ -115,8 +121,8 @@ def elaborate(self, platform):
115121 ]
116122
117123 m .submodules += [
118- FFSynchronizer (clock_buffer .i , self .clock_i , init = 1 ),
119- FFSynchronizer (data_buffer .i , self .data_i , init = 1 ),
124+ cdc . FFSynchronizer (clock_buffer .i , self .clock_i , init = 1 ),
125+ cdc . FFSynchronizer (data_buffer .i , self .data_i , init = 1 ),
120126 ]
121127
122128 clock_s = Signal (init = 1 )
@@ -135,19 +141,21 @@ def elaborate(self, platform):
135141 return m
136142
137143
138- class PS2HostController (Elaboratable ):
139- def __init__ ( self , bus ):
140- self . bus = bus
144+ class PS2HostController (wiring . Component ):
145+ en : In ( 1 ) # whether communication should be allowed or inhibited
146+ stb : Out ( 1 ) # strobed for 1 cycle after each stop bit to indicate an update
141147
142- self .en = Signal () # whether communication should be allowed or inhibited
143- self .stb = Signal () # strobed for 1 cycle after each stop bit to indicate an update
148+ i_valid : In (1 ) # whether i_data is to be transmitted
149+ i_data : In (8 ) # data byte written to device
150+ i_ack : Out (1 ) # whether the device acked the data ("line control" bit)
144151
145- self .i_valid = Signal () # whether i_data is to be transmitted
146- self .i_data = Signal (8 ) # data byte written to device
147- self .i_ack = Signal () # whether the device acked the data ("line control" bit)
152+ o_valid : Out (1 ) # whether o_data has been received correctly
153+ o_data : Out (8 ) # data byte read from device
154+
155+ def __init__ (self , bus ):
156+ self .bus = bus
148157
149- self .o_valid = Signal () # whether o_data has been received correctly
150- self .o_data = Signal (8 ) # data byte read from device
158+ super ().__init__ ()
151159
152160 def elaborate (self , platform ):
153161 m = Module ()
@@ -224,32 +232,35 @@ def elaborate(self, platform):
224232 return m
225233
226234
227- class PS2HostSubtarget (Elaboratable ):
228- def __init__ (self , ports , in_fifo , out_fifo , inhibit_cyc ):
229- self .ports = ports
230- self .in_fifo = in_fifo
231- self .out_fifo = out_fifo
232- self .inhibit_cyc = inhibit_cyc
235+ class PS2HostComponent (wiring .Component ):
236+ i_stream : In (stream .Signature (8 ))
237+ o_stream : Out (stream .Signature (8 ))
238+
239+ def __init__ (self , ports , * , inhibit_cyc ):
240+ self ._ports = ports
241+ self ._inhibit_cyc = inhibit_cyc
242+
243+ super ().__init__ ()
233244
234245 def elaborate (self , platform ):
235246 m = Module ()
236247
237- m .submodules .bus = bus = PS2Bus (self .ports )
248+ m .submodules .bus = bus = PS2Bus (self ._ports )
238249 m .submodules .ctrl = ctrl = PS2HostController (bus )
239250
240- timer = Signal (range (self .inhibit_cyc ))
251+ timer = Signal (range (self ._inhibit_cyc ))
241252 count = Signal (7 )
242253 error = Signal ()
243254
244255 with m .FSM ():
245256 with m .State ("RECV-COMMAND" ):
246- m .d .comb += self .out_fifo . r_en .eq (1 )
247- with m .If (self .out_fifo . r_rdy ):
257+ m .d .comb += self .i_stream . ready .eq (1 )
258+ with m .If (self .i_stream . valid ):
248259 m .d .sync += [
249- count .eq (self .out_fifo . r_data [:7 ]),
260+ count .eq (self .i_stream . payload [:7 ]),
250261 error .eq (0 ),
251262 ]
252- with m .If (self .out_fifo . r_data [7 ]):
263+ with m .If (self .i_stream . payload [7 ]):
253264 m .d .sync += ctrl .i_valid .eq (1 )
254265 m .next = "WRITE-BYTE"
255266 with m .Else ():
@@ -260,10 +271,10 @@ def elaborate(self, platform):
260271 m .next = "READ-WAIT"
261272
262273 with m .State ("WRITE-BYTE" ):
263- m .d .comb += self .out_fifo . r_en .eq (1 )
264- with m .If (self .out_fifo . r_rdy ):
274+ m .d .comb += self .i_stream . ready .eq (1 )
275+ with m .If (self .i_stream . valid ):
265276 m .d .sync += [
266- ctrl .i_data .eq (self .out_fifo . r_data ),
277+ ctrl .i_data .eq (self .i_stream . payload ),
267278 ctrl .en .eq (1 ),
268279 ]
269280 m .next = "WRITE-WAIT"
@@ -275,8 +286,8 @@ def elaborate(self, platform):
275286 # You better be reading from that FIFO. (But the FIFO is 4× larger than the largest
276287 # possible command response, so it's never a race.)
277288 m .d .comb += [
278- self .in_fifo . w_en .eq (1 ),
279- self .in_fifo . w_data .eq (ctrl .i_ack ),
289+ self .o_stream . valid .eq (1 ),
290+ self .o_stream . payload .eq (ctrl .i_ack ),
280291 ]
281292 with m .If ((count == 0 ) | ~ ctrl .i_ack ):
282293 m .next = "INHIBIT"
@@ -290,8 +301,8 @@ def elaborate(self, platform):
290301 m .next = "READ-BYTE"
291302 with m .State ("READ-BYTE" ):
292303 m .d .comb += [
293- self .in_fifo . w_en .eq (1 ),
294- self .in_fifo . w_data .eq (ctrl .o_data ),
304+ self .o_stream . valid .eq (1 ),
305+ self .o_stream . payload .eq (ctrl .o_data ),
295306 ]
296307 with m .If (~ ctrl .o_valid & (error == 0 )):
297308 m .d .sync += error .eq (count )
@@ -302,8 +313,8 @@ def elaborate(self, platform):
302313
303314 with m .State ("SEND-ERROR" ):
304315 m .d .comb += [
305- self .in_fifo . w_en .eq (1 ),
306- self .in_fifo . w_data .eq (error ),
316+ self .o_stream . valid .eq (1 ),
317+ self .o_stream . payload .eq (error ),
307318 ]
308319 m .next = "INHIBIT"
309320
@@ -312,7 +323,7 @@ def elaborate(self, platform):
312323 # sending two back-to-back commands (or a command and a read, etc).
313324 m .d .sync += [
314325 ctrl .en .eq (0 ),
315- timer .eq (self .inhibit_cyc ),
326+ timer .eq (self ._inhibit_cyc - 1 ),
316327 ]
317328 m .next = "INHIBIT-WAIT"
318329
@@ -330,10 +341,17 @@ class PS2HostError(GlasgowAppletError):
330341
331342
332343class PS2HostInterface :
333- def __init__ (self , interface , logger ):
334- self ._lower = interface
344+ def __init__ (self , logger , assembly , * , clock , data , reset ):
335345 self ._logger = logger
336346 self ._level = logging .DEBUG if self ._logger .name == __name__ else logging .TRACE
347+
348+ inhibit_cyc = round (60e-6 / assembly .sys_clk_period )
349+
350+ ports = assembly .add_port_group (clock = clock , data = data , reset = reset )
351+ assembly .use_pulls ({clock : "high" , data : "high" })
352+ component = assembly .add_submodule (PS2HostComponent (ports , inhibit_cyc = inhibit_cyc ))
353+ self ._pipe = assembly .add_inout_pipe (component .o_stream , component .i_stream )
354+
337355 self ._streaming = False
338356
339357 def _log (self , message , * args ):
@@ -342,13 +360,14 @@ def _log(self, message, *args):
342360 async def send_command (self , cmd , ret = 0 ):
343361 assert ret < 0x7f
344362 assert not self ._streaming
345- await self ._lower .write ([0x80 | (ret + 1 ), cmd ])
346- line_ack , = await self ._lower .read (1 )
363+ await self ._pipe .send ([0x80 | (ret + 1 ), cmd ])
364+ await self ._pipe .flush ()
365+ line_ack , = await self ._pipe .recv (1 )
347366 if not line_ack :
348367 self ._log ("cmd=%02x nak" , cmd )
349368 raise PS2HostError ("peripheral did not acknowledge command {:#04x}"
350369 .format (cmd ))
351- cmd_ack , * result , error = await self ._lower . read (1 + ret + 1 )
370+ cmd_ack , * result , error = await self ._pipe . recv (1 + ret + 1 )
352371 result = bytes (result )
353372 self ._log ("cmd=%02x ack=%02x ret=<%s>" , cmd , cmd_ack , result .hex ())
354373 if error > 0 :
@@ -373,8 +392,9 @@ async def send_command(self, cmd, ret=0):
373392 async def recv_packet (self , ret ):
374393 assert ret < 0x7f
375394 assert not self ._streaming
376- await self ._lower .write ([ret ])
377- * result , error = await self ._lower .read (ret + 1 )
395+ await self ._pipe .send ([ret ])
396+ await self ._pipe .flush ()
397+ * result , error = await self ._pipe .recv (ret + 1 )
378398 result = bytes (result )
379399 self ._log ("ret=<%s>" , result .hex ())
380400 if error > 0 :
@@ -385,12 +405,13 @@ async def recv_packet(self, ret):
385405 async def stream (self , callback ):
386406 assert not self ._streaming
387407 self ._streaming = True
388- await self ._lower .write ([0x7f ])
408+ await self ._pipe .send ([0x7f ])
409+ await self ._pipe .flush ()
389410 while True :
390- await callback (* await self ._lower . read (1 ))
411+ await callback (* await self ._pipe . recv (1 ))
391412
392413
393- class PS2HostApplet (GlasgowApplet ):
414+ class PS2HostApplet (GlasgowAppletV2 ):
394415 logger = logging .getLogger (__name__ )
395416 help = "communicate with IBM PS/2 peripherals"
396417 description = """
@@ -404,44 +425,31 @@ class PS2HostApplet(GlasgowApplet):
404425
405426 @classmethod
406427 def add_build_arguments (cls , parser , access ):
407- super ().add_build_arguments (parser , access )
408-
428+ access .add_voltage_argument (parser )
409429 access .add_pins_argument (parser , "clock" , default = True )
410430 access .add_pins_argument (parser , "data" , default = True )
411431 access .add_pins_argument (parser , "reset" )
412432
413- def build (self , target , args ):
414- self .mux_interface = iface = target .multiplexer .claim_interface (self , args )
415- subtarget = iface .add_subtarget (PS2HostSubtarget (
416- ports = iface .get_port_group (
417- clock = args .clock ,
418- data = args .data ,
419- reset = args .reset
420- ),
421- in_fifo = iface .get_in_fifo (),
422- out_fifo = iface .get_out_fifo (),
423- inhibit_cyc = int (target .sys_clk_freq * 60e-6 ),
424- ))
425-
426- async def run (self , device , args ):
427- iface = await device .demultiplexer .claim_interface (self , self .mux_interface , args ,
428- pull_high = {args .clock , args .data })
429- return PS2HostInterface (iface , self .logger )
433+ def build (self , args ):
434+ with self .assembly .add_applet (self ):
435+ self .assembly .use_voltage (args .voltage )
436+ self .ps2_iface = PS2HostInterface (self .logger , self .assembly ,
437+ clock = args .clock , data = args .data , reset = args .reset )
430438
431439 @classmethod
432- def add_interact_arguments (cls , parser ):
440+ def add_run_arguments (cls , parser ):
433441 def hex_bytes (arg ):
434442 return bytes .fromhex (arg )
435443 parser .add_argument (
436444 "init" , metavar = "INIT" , type = hex_bytes , nargs = "?" , default = b"" ,
437445 help = "send each byte from INIT as an initialization command" )
438446
439- async def interact (self , device , args , iface ):
447+ async def run (self , args ):
440448 for init_byte in args .init :
441- await iface .send_command (init_byte )
449+ await self . ps2_iface .send_command (init_byte )
442450 async def print_byte (byte ):
443451 print (f"{ byte :02x} " , end = " " , flush = True )
444- await iface .stream (print_byte )
452+ await self . ps2_iface .stream (print_byte )
445453
446454 @classmethod
447455 def tests (cls ):
0 commit comments