Skip to content

Commit 9e02e73

Browse files
committed
Merge branch 'leep_literal' into 'master'
Added more flexible CLI options for LEEP python package * Added name:size CLI syntax for reading a specified number of elements, rather than the whole 2**aw range * Supports register access by name or by explicit address (in Python integer syntax, i.e. 0xabc, 0b110, or 123) * If a romx is not found, no Exception is raised as registers can still be accessed by address (covers functionality of lbus_access.py) * Added name+offset CLI syntax to offset the base address by an integer. This can be combined with other syntax elements, like name+offset:size or name+offset=val * Preserves multiple sequential writes via comma-separated values, which now can optionally begin at an offset, i.e. name+offset=val0,val1,val2 * Removes the requirement that writes must fill the entire size of the element (which was previously enforced by an assert). See merge request hdl-libraries/bedrock!200
2 parents fda69d5 + 01a0da6 commit 9e02e73

File tree

10 files changed

+581
-70
lines changed

10 files changed

+581
-70
lines changed

.gitlab-ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ include:
3838
- local: .gitlab/ci/cdc_check.gitlab-ci.yml
3939
- local: .gitlab/ci/localbus.gitlab-ci.yml
4040
- local: .gitlab/ci/ctrace.gitlab-ci.yml
41+
- local: .gitlab/ci/leep.gitlab-ci.yml
4142

4243
leep_test:
4344
script:
44-
- cd projects/common && python3 -m unittest -v
45+
- cd projects/common && PYTHONPATH=../../build-tools python3 -m unittest -v
4546

4647
flake8:
4748
stage: test

.gitlab/ci/leep.gitlab-ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
leep_test2:
2+
stage: test
3+
script:
4+
- make -C projects/common/leep && make -C projects/common/leep clean

badger/tests/cluster_run.gold

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
a3633a315dc8a61e6c2c81b0433258e71dcb43ad
22
a3633a315dc8a61e6c2c81b0433258e71dcb43ad
33
a3633a315dc8a61e6c2c81b0433258e71dcb43ad
4-
scratch_out 00001e61
5-
scratch_in_r 00001e61
6-
scratch_out 000022b8
7-
scratch_in_r 000022b8
8-
scratch_out 0000270f
9-
scratch_in_r 0000270f
4+
scratch_out 1e61
5+
scratch_in_r 1e61
6+
scratch_out 22b8
7+
scratch_in_r 22b8
8+
scratch_out 270f
9+
scratch_in_r 270f

projects/common/leep/Makefile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# A Makefile to run LEEP tests
2+
THIS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
3+
include $(THIS_DIR)/../../../dir_list.mk
4+
5+
PYTHON=python3
6+
7+
LEEP_CORE=base.py raw.py ca.py file.py logic.py
8+
9+
all: test_cli
10+
11+
test_cli: $(LEEP_CORE) cli.py
12+
PYTHONPATH="$(THIS_DIR)/..:$(BUILD_DIR)" $(PYTHON) -m leep.test.test_cli test
13+
14+
# This is a test target currently only used for manual testing. Development is in progress
15+
# for including this in automated regression tests.
16+
server: $(LEEP_CORE) cli.py
17+
PYTHONPATH="$(THIS_DIR)/..:$(BUILD_DIR)" $(PYTHON) -m leep.test.test_cli server
18+
19+
CLEANS=test.json
20+
21+
clean:
22+
rm -rf $(CLEANS)

projects/common/leep/base.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,35 @@ def wrapper(*args, **kwargs):
2626
regs = args[1]
2727
if len(regs) and isinstance(regs[0], str):
2828
# it's a read operation
29-
for reg in regs:
30-
print('reading register {}'.format(reg))
29+
for n in range(len(regs)):
30+
reg = regs[n][0]
31+
if len(regs) > 1:
32+
offset = regs[n][1]
33+
try:
34+
reg = "from address 0x{:x}".format(reg)
35+
except ValueError:
36+
reg = "register {}".format(reg)
37+
if offset is not None and offset != 0:
38+
offsetstr = " (offset {})".format(offset)
39+
else:
40+
offsetstr = ""
41+
print('reading {}{}'.format(reg, offsetstr))
3142
else:
3243
# it's a write operation, 'regs' is now a tuple, not str
33-
for reg, val in regs:
34-
print('writing {} to register {}'.format(val, reg))
44+
offset = 0
45+
for n in range(len(regs)):
46+
reg, val = regs[n][:2]
47+
if len(regs) > 2:
48+
offset = regs[n][2]
49+
try:
50+
reg = "address 0x{:x}".format(reg)
51+
except ValueError:
52+
reg = "register {}".format(reg)
53+
if offset is not None and offset != 0:
54+
offsetstr = " (offset {})".format(offset)
55+
else:
56+
offsetstr = ""
57+
print('writing {} to {}{}'.format(val, reg, offsetstr))
3558
return fcn(*args, **kwargs)
3659
return wrapper
3760

projects/common/leep/cli.py

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tempfile
88
import shutil
99
import ast
10+
import re
1011

1112
from collections import defaultdict
1213

@@ -17,19 +18,103 @@
1718
_log = logging.getLogger(__name__)
1819

1920

21+
def _int(s):
22+
return int(ast.literal_eval(s))
23+
24+
25+
def _expandWriteVals(ss):
26+
"""Convert string 'n,m,l,k,...' into list of ints [n, m, l, k, ...]
27+
Raise Exception if not all items separated by commas can be interpreted as integers."""
28+
if ',' not in ss:
29+
return _int(ss)
30+
vals = [_int(x) for x in ss.split(',')]
31+
return vals
32+
33+
34+
def parseTransaction(xact):
35+
"""
36+
Parse transaction string from CLI. Returns (str/int reg, int offset, int/None size, int/None write_val)
37+
If 'reg' is type int, it is an explicit base address.
38+
If 'size' is None, size = 1
39+
If 'reg' is type str, it is a register name (supposedly).
40+
If 'size' is None, size = 2**aw (where 'aw' is the 'addr_width' from the romx JSON)
41+
If 'write_val' is None, it's a read transaction, else it's a write of value 'write_val'
42+
Viable formats for a transaction (read/write) in string form:
43+
Example Implied Transaction
44+
-------------------------------------------
45+
regname Read from named register (str) 'regname'
46+
regaddr Read from explicit address (int) 'regaddr'
47+
regname=val Write (int) 'val' to named register (str) 'regname'
48+
regaddr=val Write (int) 'val' to explicit address (int) 'regaddr'
49+
regname=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at the
50+
address of named register (str) 'regname'
51+
regaddr=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at
52+
address (int) 'regaddr'
53+
regname+offset Read from address = romx['regname']['base_addr'] + (int) 'offset'
54+
regaddr+offset Read from address = (int) 'regaddr' + (int) 'offset'
55+
regname:size Read (int) 'size' elements starting from address romx['regname']['base_addr']
56+
regaddr:size Read (int) 'size' elements starting from (int) 'regaddr'
57+
regname+offset=val Write (int) 'val' to address romx['regname']['base_addr'] + (int) 'offset'
58+
regname+offset=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at
59+
address romx['regname']['base_addr'] + (int) 'offset'
60+
regaddr+offset=val Write (int) 'val' to address (int) 'regaddr' + (int) 'offset'
61+
regaddr+offset=val0,...,valN Write (int) 'val0' through 'valN' to consecutive addresses beginning at
62+
address (int) 'regaddr'
63+
regname+offset:size Read (int) 'size' elements starting from address romx['regname']['base_addr'] + \
64+
(int) 'offset'
65+
regaddr+offset:size Read (int) 'size' elements starting from (int) 'regaddr' + (int) 'offset'
66+
I'm not sure what use case the "regaddr+offset" syntax supports, but it does no harm to include it.
67+
NOTE! Deliberately not supporting "regname-offset" (negative offsets) as it's use case is unclear
68+
and a '_' to '-' typo could potentially collide with legitimate transactions.
69+
"""
70+
restr = r"(\w+)\s*([=+:])?\s*([0-9a-fA-Fx,\-]+)?\s*([=:])?\s*([0-9a-fA-Fx,\-]+)?"
71+
_match = re.match(restr, xact)
72+
offset = 0
73+
wval = None
74+
size = None
75+
if _match:
76+
groups = _match.groups()
77+
regstr = groups[0] # Always starts with 'regname' or 'regaddr'
78+
if groups[1] == '=':
79+
wval = _expandWriteVals(groups[2])
80+
elif groups[1] == '+':
81+
offset = _int(groups[2])
82+
elif groups[1] == ':':
83+
size = _int(groups[2])
84+
if groups[3] == '=':
85+
if groups[1] == '=':
86+
raise Exception("Malformed transaction: {}".format(xact))
87+
wval = _expandWriteVals(groups[4])
88+
elif groups[3] == ':':
89+
if size is not None:
90+
raise Exception("Malformed transaction: {}".format(xact))
91+
size = _int(groups[4])
92+
else:
93+
raise Exception("Failed to match: {}".format(xact))
94+
try:
95+
reg = _int(regstr)
96+
except ValueError:
97+
reg = regstr
98+
if size is None:
99+
if wval is None:
100+
size = None
101+
else:
102+
size = 0
103+
return (reg, offset, size, wval)
104+
105+
20106
def readwrite(args, dev):
21-
for pair in args.reg:
22-
name, _eq, val = pair.partition('=')
23-
if len(val):
24-
val = ast.literal_eval(val)
25-
dev.reg_write([(name, val)])
107+
for xact in args.reg:
108+
reg, offset, size, wvals = parseTransaction(xact)
109+
if wvals is not None:
110+
dev.reg_write_offset([(reg, wvals, offset)])
26111
else:
27-
value, = dev.reg_read((name,))
112+
value, = dev.reg_read_size(((reg, size, offset),))
28113
try:
29114
_ = iter(value)
30-
print("%s \t%s" % (name, ' '.join(['%x' % v for v in value])))
115+
print("%s \t%s" % (reg, ' '.join(['%x' % v for v in value])))
31116
except TypeError:
32-
print("%s \t%08x" % (name, value))
117+
print("%s \t%x" % (reg, value))
33118

34119

35120
def listreg(args, dev):

0 commit comments

Comments
 (0)