Skip to content

Commit 9069581

Browse files
committed
debug.superh.aud: Add SuperH AUD-II read support
1 parent 18442e9 commit 9069581

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ MCU debugging
99
:maxdepth: 3
1010

1111
arm7
12+
superh
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
``debug-superh-aud``
2+
==============
3+
4+
CLI reference
5+
-------------
6+
7+
.. _applet.debug.superh.aud:
8+
9+
.. autoprogram:: glasgow.applet.debug.superh.aud:AUDApplet._get_argparser_for_sphinx("debug-superh-aud")
10+
:prog: glasgow run debug-superh-aud

software/glasgow/applet/debug/superh/__init__.py

Whitespace-only changes.
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
# Ref: SH7254R Group User's Manual: Hardware §21.1 §21.4
2+
# Document Number: R01UH0480EJ0400
3+
# Accession: G00106
4+
#
5+
# This applet implements part of the SuperH AUD-II protocol. This protocol
6+
# supports rich features such as branch and data tracing. However, it also
7+
# supports a "RAM Monitoring Mode" wich has simple read and write command. This
8+
# applet implements the RAM Monitoring Mode read command to be able to dump a
9+
# target's memory.
10+
11+
import logging
12+
import sys
13+
14+
from amaranth import *
15+
from amaranth.lib import io, wiring, stream, cdc, enum
16+
from amaranth.lib.wiring import In, Out
17+
18+
from glasgow.abstract import AbstractAssembly, GlasgowPin
19+
from glasgow.applet import GlasgowAppletV2
20+
21+
class AUDCommand(enum.Enum, shape=8):
22+
Reset = 0x00
23+
Run = 0x01
24+
Sync = 0x02
25+
Out = 0x03
26+
Inp = 0x04
27+
28+
class AUDComponent(wiring.Component):
29+
i_stream: In(stream.Signature(8))
30+
o_stream: Out(stream.Signature(8))
31+
32+
def __init__(self, ports, period_cyc):
33+
self._ports = ports
34+
self._period_cyc = period_cyc
35+
36+
super().__init__()
37+
38+
def elaborate(self, platform):
39+
m = Module()
40+
41+
# Add IO buffers
42+
m.submodules.audsync_buffer = io.Buffer("o", self._ports.audsync)
43+
m.submodules.audmd_buffer = io.Buffer("o", self._ports.audmd)
44+
m.submodules.audrst_buffer = io.Buffer("o", self._ports.audrst)
45+
m.submodules.audck_buffer = io.Buffer("o", self._ports.audck)
46+
data_pins = []
47+
for i, pin in enumerate(self._ports.audata):
48+
m.submodules[f"audata{i}_buffer"] = pin = io.Buffer("io", pin)
49+
data_pins.append(pin)
50+
51+
# Create signals for the IO
52+
audata_o = Signal(4)
53+
audata_oe = Signal(4)
54+
audata_i = Signal(4)
55+
audsync = Signal()
56+
audrst = Signal()
57+
audck = Signal()
58+
59+
# Connect the IO buffers to the signals
60+
m.d.comb += m.submodules.audmd_buffer.o.eq(1) # Always in RAM monitoring mode
61+
m.d.comb += m.submodules.audrst_buffer.o.eq(audrst)
62+
m.d.comb += m.submodules.audck_buffer.o.eq(audck)
63+
m.d.comb += m.submodules.audsync_buffer.o.eq(audsync)
64+
m.d.comb += [
65+
Cat(pin.oe for pin in data_pins).eq(audata_oe),
66+
Cat(pin.o for pin in data_pins).eq(audata_o),
67+
]
68+
m.submodules += cdc.FFSynchronizer(Cat(pin.i for pin in data_pins), audata_i)
69+
70+
# FSM related signals
71+
timer = Signal(range(self._period_cyc))
72+
data = Signal(4)
73+
74+
# Main State Machine
75+
with m.FSM():
76+
# Receive command and switch to the appropriate handler state
77+
with m.State("RECV-COMMAND"):
78+
with m.If(self.i_stream.valid):
79+
m.d.comb += self.i_stream.ready.eq(1)
80+
m.d.sync += data.eq(self.i_stream.payload >> 4)
81+
with m.If((self.i_stream.payload & 0xF) == AUDCommand.Reset):
82+
m.next = "RESET"
83+
with m.If((self.i_stream.payload & 0xF) == AUDCommand.Run):
84+
m.next = "RUN"
85+
with m.If((self.i_stream.payload & 0xF) == AUDCommand.Sync):
86+
m.next = "SYNC"
87+
with m.Elif((self.i_stream.payload & 0xF) == AUDCommand.Out):
88+
m.next = "OUT"
89+
with m.Elif((self.i_stream.payload & 0xF) == AUDCommand.Inp):
90+
m.next = "INP"
91+
92+
# Assert Reset and put pins into a known state
93+
with m.State("RESET"):
94+
# Put pins into known state
95+
m.d.sync += audck.eq(0)
96+
m.d.sync += audata_oe.eq(0b1111)
97+
m.d.sync += audata_o.eq(0b0000)
98+
m.d.sync += audsync.eq(1)
99+
100+
# Put into reset
101+
m.d.sync += audrst.eq(0)
102+
m.next = "RECV-COMMAND"
103+
104+
# Release Reset
105+
with m.State("RUN"):
106+
# Release reset
107+
m.d.sync += audrst.eq(1)
108+
m.next = "RECV-COMMAND"
109+
110+
# Set Sync pin to provided value
111+
with m.State("SYNC"):
112+
m.d.sync += audsync.eq(data != 0b0000)
113+
m.next = "RECV-COMMAND"
114+
115+
# Send 1 nibble of data on the bus, then strobe clock
116+
with m.State("OUT"):
117+
m.d.sync += audata_oe.eq(0b1111) # Switch AUDATA pins to output
118+
m.d.sync += audata_o.eq(data)
119+
120+
# Strobe clock
121+
m.d.sync += audck.eq(0)
122+
m.d.sync += timer.eq(self._period_cyc - 1)
123+
m.next = "OUT-CLOCK-0"
124+
125+
# Wait for clock period to pass, then set clock high
126+
with m.State("OUT-CLOCK-0"):
127+
with m.If(timer == 0):
128+
m.d.sync += audck.eq(1)
129+
m.d.sync += timer.eq(self._period_cyc - 1)
130+
m.next = "OUT-CLOCK-1"
131+
with m.Else():
132+
m.d.sync += timer.eq(timer - 1)
133+
134+
# Wait for clock period to pass, then return to command reception
135+
with m.State("OUT-CLOCK-1"):
136+
with m.If(timer == 0):
137+
m.next = "RECV-COMMAND"
138+
with m.Else():
139+
m.d.sync += timer.eq(timer - 1)
140+
141+
# Strobe clock, then read data on the rising edge. Send to PC
142+
with m.State("INP"):
143+
m.d.sync += audata_oe.eq(0b0000)
144+
145+
# Strobe clock
146+
m.d.sync += audck.eq(0)
147+
m.d.sync += timer.eq(self._period_cyc - 1)
148+
m.next = "INP-CLOCK-0"
149+
150+
# Wait for clock period to pass, then sample data and set clock high
151+
with m.State("INP-CLOCK-0"):
152+
with m.If(timer == 0):
153+
m.d.sync += audck.eq(1)
154+
m.d.sync += timer.eq(self._period_cyc - 1)
155+
156+
# Sample data on rising edge
157+
m.d.sync += data.eq(audata_i)
158+
159+
m.next = "INP-CLOCK-1"
160+
with m.Else():
161+
m.d.sync += timer.eq(timer - 1)
162+
163+
# Wait for clock period to pass, then send data to PC
164+
with m.State("INP-CLOCK-1"):
165+
with m.If(timer == 0):
166+
m.next = "SEND-DATA"
167+
with m.Else():
168+
m.d.sync += timer.eq(timer - 1)
169+
170+
# Send the sampled data to the output stream, return to command reception
171+
with m.State("SEND-DATA"):
172+
m.d.comb += self.o_stream.valid.eq(1)
173+
m.d.comb += self.o_stream.payload.eq(data)
174+
175+
with m.If(self.o_stream.ready):
176+
m.next = "RECV-COMMAND"
177+
178+
return m
179+
180+
class AUDInterface:
181+
def __init__(self, logger: logging.Logger, assembly: AbstractAssembly, *,
182+
audata: GlasgowPin, audsync: GlasgowPin, audck: GlasgowPin, audmd: GlasgowPin, audrst: GlasgowPin, frequency: int):
183+
self._logger = logger
184+
self._level = logging.TRACE
185+
186+
ports = assembly.add_port_group(audata=audata, audsync=audsync, audck=audck, audmd=audmd, audrst=audrst)
187+
component = assembly.add_submodule(AUDComponent(
188+
ports,
189+
period_cyc=round(1 / (assembly.sys_clk_period * frequency)),
190+
))
191+
self._pipe = assembly.add_inout_pipe(component.o_stream, component.i_stream)
192+
193+
async def _cmd(self, cmd: AUDCommand, val=0):
194+
assert val <= 0xF, "Value must be less than 0xF"
195+
await self._pipe.send([cmd.value | (val << 4)])
196+
197+
async def reset(self):
198+
await self._cmd(AUDCommand.Reset)
199+
200+
async def run(self):
201+
await self._cmd(AUDCommand.Run)
202+
203+
async def out(self, val):
204+
await self._cmd(AUDCommand.Out, val)
205+
206+
async def inp(self):
207+
await self._cmd(AUDCommand.Inp)
208+
209+
# This is the only place we need to flush, as we're going to wait for a response
210+
await self._pipe.flush()
211+
212+
data = await self._pipe.recv(1)
213+
return data[0]
214+
215+
async def sync(self, val):
216+
await self._cmd(AUDCommand.Sync, val)
217+
218+
async def init(self):
219+
await self.reset()
220+
221+
# Strobe clock a couple times
222+
for i in range(10):
223+
await self.out(0)
224+
225+
await self.run()
226+
227+
# Strobe clock a couple times
228+
for i in range(10):
229+
await self.out(0)
230+
231+
async def read(self, addr, sz=4, timeout=100):
232+
await self.sync(0)
233+
await self.out(0)
234+
235+
match sz:
236+
case 1:
237+
await self.out(0b1000) # Read byte
238+
case 2:
239+
await self.out(0b1001) # Read word
240+
case 4:
241+
await self.out(0b1010) # Read longword
242+
case _:
243+
raise ValueError("Invalid size, must be 1, 2, or 4 bytes")
244+
245+
# Clock out Addr
246+
for i in range(8):
247+
await self.out((addr >> (i * 4)) & 0b1111)
248+
249+
# Wait for data ready
250+
for _ in range(timeout):
251+
data = await self.inp()
252+
if data == 1:
253+
break
254+
else:
255+
raise RuntimeError(f"Timeout waiting for data ready. Got {data:#x}")
256+
257+
# Set AUDSYNC high to indicate we're ready to read
258+
await self.sync(1)
259+
await self.inp()
260+
261+
# Clock in the data
262+
out = 0
263+
for i in range(2*sz):
264+
out |= (await self.inp() << (i * 4))
265+
return out.to_bytes(sz, byteorder='big')
266+
267+
268+
class AUDApplet(GlasgowAppletV2):
269+
logger = logging.getLogger(__name__)
270+
help = "SuperH AUD-II Applet"
271+
description = """
272+
Read memory using the SuperH AUD-II protocol.
273+
"""
274+
@classmethod
275+
def add_build_arguments(cls, parser, access):
276+
def auto_int(x):
277+
return int(x, 0)
278+
279+
access.add_voltage_argument(parser)
280+
281+
access.add_pins_argument(parser, "audata", width=4, required=True)
282+
access.add_pins_argument(parser, "audsync", required=True)
283+
access.add_pins_argument(parser, "audck", required=True)
284+
access.add_pins_argument(parser, "audrst", required=True)
285+
access.add_pins_argument(parser, "audmd", required=True)
286+
287+
parser.add_argument(
288+
"-f", "--frequency", metavar="FREQ", type=int, default=100,
289+
help="set clock period to FREQ kHz (default: %(default)s)")
290+
291+
parser.add_argument(
292+
"-a", "--address", type=auto_int, required=True,
293+
help="Starting address to read from, e.g. 0x0"
294+
)
295+
parser.add_argument(
296+
"-s", "--size", type=auto_int, required=True,
297+
help="Size of the data to read in bytes, e.g. 0x80000"
298+
)
299+
parser.add_argument(
300+
"-o", "--output", required=True,
301+
help="Filename to write the output to"
302+
)
303+
304+
305+
def build(self, args):
306+
with self.assembly.add_applet(self):
307+
self.assembly.use_voltage(args.voltage)
308+
self.aud_iface = AUDInterface(
309+
self.logger,
310+
self.assembly,
311+
audata=args.audata,
312+
audsync=args.audsync,
313+
audck=args.audck,
314+
audmd=args.audmd,
315+
audrst=args.audrst,
316+
frequency=args.frequency * 1000)
317+
318+
@staticmethod
319+
def _show_progress(done, total, status):
320+
if sys.stdout.isatty():
321+
sys.stdout.write("\r\033[0K")
322+
if done < total:
323+
sys.stdout.write(f"{done}/{total} bytes done ({done / total * 100:.2f}%)")
324+
if status:
325+
sys.stdout.write(f"; {status}")
326+
sys.stdout.flush()
327+
328+
async def run(self, args):
329+
self.logger.info("Initializing AUD-II interface")
330+
await self.aud_iface.init()
331+
332+
self.logger.info("Reading data")
333+
bs = 4
334+
with open(args.output, 'wb') as f:
335+
for i in range(args.address, args.address + args.size, bs):
336+
data = await self.aud_iface.read(i, sz=bs)
337+
f.write(data)
338+
self._show_progress(i - args.address + bs, args.size, f"Read {data.hex()}")
339+
340+
self.logger.info("Done")

software/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ debug-arc = "glasgow.applet.debug.arc:DebugARCApplet"
118118
debug-arm = "glasgow.applet.debug.arm.jtag:DebugARMJTAGApplet"
119119
debug-arm7 = "glasgow.applet.debug.arm.arm7:DebugARM7Applet"
120120
debug-mips = "glasgow.applet.debug.mips:DebugMIPSApplet"
121+
debug-superh-aud = "glasgow.applet.debug.superh.aud:AUDApplet"
121122

122123
program-avr-spi = "glasgow.applet.program.avr.spi:ProgramAVRSPIApplet"
123124
program-ice40-flash = "glasgow.applet.program.ice40_flash:ProgramICE40FlashApplet"

0 commit comments

Comments
 (0)