Skip to content

Commit c26d3b1

Browse files
committed
applet.interface.ps2_host: migrate to V2 API.
1 parent f180457 commit c26d3b1

File tree

9 files changed

+185
-121
lines changed

9 files changed

+185
-121
lines changed

docs/manual/src/applets/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ Applet index
1313
program/index
1414
debug/index
1515
control/index
16+
sensor/index
1617
internal/index

docs/manual/src/applets/interface/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ I/O interfaces
1717
jtag_xvc
1818
swd_probe
1919
probe_rs
20+
ps2_host
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
``ps2-host``
2+
============
3+
4+
.. _applet.interface.ps2_host:
5+
6+
.. autoprogram:: glasgow.applet.interface.ps2_host:PS2HostApplet._get_argparser_for_sphinx("ps2-host")
7+
:prog: glasgow run ps2-host
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.. _applet.sensor:
2+
3+
Sensor data collection
4+
======================
5+
6+
.. automodule:: glasgow.applet.sensor
7+
8+
.. toctree::
9+
:maxdepth: 2
10+
11+
mouse_ps2
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
``sensor-mouse-ps2``
2+
====================
3+
4+
.. _applet.sensor.mouse_ps2:
5+
6+
.. autoprogram:: glasgow.applet.sensor.mouse_ps2:SensorMousePS2Applet._get_argparser_for_sphinx("sensor-mouse-ps2")
7+
:prog: glasgow run sensor-mouse-ps2

software/glasgow/applet/interface/ps2_host/__init__.py

Lines changed: 86 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
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.
@@ -61,11 +61,15 @@
6161

6262
import logging
6363
import asyncio
64+
6465
from 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

332343
class 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):
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from ... import *
1+
from glasgow.applet import GlasgowAppletV2TestCase, synthesis_test
22
from . import PS2HostApplet
33

44

5-
class PS2HostAppletTestCase(GlasgowAppletTestCase, applet=PS2HostApplet):
5+
class PS2HostAppletTestCase(GlasgowAppletV2TestCase, applet=PS2HostApplet):
66
@synthesis_test
77
def test_build(self):
88
self.assertBuilds()

0 commit comments

Comments
 (0)