Skip to content

Commit

Permalink
add feed_logic substitution generator
Browse files Browse the repository at this point in the history
  • Loading branch information
mdavidsaver committed Jun 22, 2019
1 parent 84df7af commit 834fa34
Show file tree
Hide file tree
Showing 8 changed files with 434 additions and 0 deletions.
2 changes: 2 additions & 0 deletions configure/CONFIG_SITE
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ CHECK_RELEASE = YES

USR_CPPFLAGS += -DUSE_TYPED_RSET

PYTHON = python

# These allow developers to override the CONFIG_SITE variable
# settings without having to modify the configure/CONFIG_SITE
# file itself.
Expand Down
3 changes: 3 additions & 0 deletions src/Db/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ DB += cav.template
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

%.substitutions: ../%.json ../feed_logic.py
$(PYTHON) ../feed_logic.py $< $@
192 changes: 192 additions & 0 deletions src/Db/feed_logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#!/usr/bin/env python3
"""
FEED acquisition device logic substitution generator
{
"signal_group":{
# required
"reset":{"name":"reg_reset","bit": 0}
,"status":{"name":"reg_status","bit": 1 }
# optional
,"readback":{
"scalar1":{"name":"reg_name"}
,"wf1":{
"name":"reg_name1"
,"max_size":8196,
,"mask":"mask_reg"
,"signals":[
{"name":"CH1"}
]
}
}
}
}
"""

from __future__ import print_function

import json, re, itertools
from collections import OrderedDict

try:
from itertools import izip as zip
except ImportError:
pass

try:
from cStringIO import StringIO
except ImportError:
from io import StringIO

def strip_comments(inp):
return re.sub(r'#.*$', '', inp, flags=re.MULTILINE)

def getargs():
from argparse import ArgumentParser
P = ArgumentParser()
P.add_argument('json', help='json config file')
P.add_argument('output', help='output .substitution file')
return P.parse_args()

def batchby(it, cnt):
grp = []
for item in it:
grp.append(item)
if len(grp)==cnt:
yield grp
grp = []
if len(grp):
yield grp

class Main(object):
def __init__(self, args):
with open(args.json, 'r') as F:
raw = F.read()

cooked = strip_comments(raw)

try:
conf = json.loads(cooked)
except:
print("Error parsing JSON")
print("======")
print(cooked)
raise

# {"file.template": [('macro', 'value'), ...], ...}
self.out = OrderedDict([
('feed_logic_trigger.template', []),
('feed_logic_read.template', []),
('feed_logic_array_mask.template', []),
('feed_logic_fanout.template', []),
('feed_logic_signal.template', []),
])

for gname, gconf in conf.items():
#out.write('### Start Signal Group: %s\n#\n'%gname)
#for line in json.dumps(gconf, indent=' ').splitlines():
# out.write('%s\n'%line)

self.signal_group(gname, gconf)

#out.write('\n### End Signal Group: %s\n'%name)

fd = StringIO()
fd.write("# Generated from:\n")
for line in raw.splitlines():
fd.write('# %s\n'%line)
fd.write("\n")

for fname, lines in self.out.items():
if not lines:
fd.write("\n# no %s\n"%fname)
continue

fd.write("""
file "%s"
{
"""%fname)

lines.reverse()
for ent in lines:
fd.write('{' + ', '.join(['%s="%s"'%(k,v) for k, v in ent.items()]) + '}\n')

fd.write("}\n")

with open(args.output, 'w') as F:
F.write(fd.getvalue())

def signal_group(self, gname, gconf):
# we append template blocks in reverse order to simplify accounting of next record.
# start with the last link in the chain, which then re-arms
nextrec = '$(PREF)%sREARM'%gname

# de-mux of signals from array registers.
# these will be synchronously processed through a set of fanouts
fanout2 = []
for rname, rconf in gconf.get('readback', {}).items():
signals = rconf.get('signals', [])
mask = hex((1<<len(signals))-1)

if mask and 'mask' in rconf:
# this register has a mask
ent = OrderedDict([
('BASE', '$(PREF)%s%s:'%(gname, rname)),
('REG', rconf['mask'])
])
mask = ent['BASE']+'MASK CP MSI'
self.out['feed_logic_array_mask.template'].append(ent)

for idx, signal in enumerate(signals):
ent = OrderedDict([
('BASE', '$(PREF)%s%s:%s'%(gname, rname, signal['name'])),
('REG', rconf['name']),
('SIZE', str(rconf.get('max_size', 8196))),
('IDX', str(idx)),
('MASK', mask),
])
fanout2.append(ent['BASE']+'E_')
self.out['feed_logic_signal.template'].append(ent)

nextfo = itertools.count(1)
fanout2 = list(batchby(fanout2, 6))
fanout2.reverse()

# emit fanouts to process all signals
for records, idx in zip(fanout2, nextfo):
ent = OrderedDict([
('NAME', '$(PREF)%sFO%d_'%(gname, idx)),
])

for n, record in enumerate(records, 1):
ent['LNK%d'%n] = record
ent['FLNK'] = nextrec
nextrec = ent['NAME']

self.out['feed_logic_fanout.template'].append(ent)

# read back registers
for rname, rconf in gconf.get('readback', {}).items():
ent = OrderedDict([
('BASE', '$(PREF)%s%s'%(gname, rname)),
('REG', rconf['name']),
('FLNK', nextrec),
])
nextrec = ent['BASE']+'E_'
self.out['feed_logic_read.template'].append(ent)


# finally the beginning
self.out['feed_logic_trigger.template'].append(OrderedDict([
('BASE', '$(PREF)'+gname),
('ARM_REG', gconf['reset']['name']),
('ARM_MASK', hex(1<<gconf['reset']['bit'])),
('RDY_REG', gconf['status']['name']),
('RDY_MASK', hex(1<<gconf['status']['bit'])),
('NEXT', nextrec),
]))


if __name__=='__main__':
args = getargs()
Main(args)
5 changes: 5 additions & 0 deletions src/Db/feed_logic_array_mask.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

record(longout, "$(BASE)MASK") {
field(DTYP, "FEED Register Write")
field(OUT , "@name=$(NAME) reg=$(REG) wait=false")
}
9 changes: 9 additions & 0 deletions src/Db/feed_logic_fanout.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
record(fanout, "$(NAME)") {
field(LNK1, "$(LNK1=)")
field(LNK2, "$(LNK2=)")
field(LNK3, "$(LNK3=)")
field(LNK4, "$(LNK4=)")
field(LNK5, "$(LNK5=)")
field(LNK6, "$(LNK6=)")
field(FLNK, "$(FLNK=)")
}
27 changes: 27 additions & 0 deletions src/Db/feed_logic_read.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Read back single device register
#
# Entry point: "$(BASE)E_"
#
# Required Macros:
# BASE - common record prefix
# NAME - FEED device instance name
# REG - register name
#
# Optional Macros:
# FLNK - Next record in chain

record(longout, "$(BASE)RD_") {
field(DTYP, "FEED Hack lp:1745039")
field(FLNK , "$(BASE)")

alias("$(BASE)E_")
}

record(longin, "$(BASE)") {
field(DTYP, "FEED Register Read")
field(DESC, "$(DESC=Reg $(NAME))")
field(INP , "@name=$(NAME) reg=$(REG)")
field(TSE , "-2")
field(FLNK, "$(FLNK=)")
field(TPRO, "$(TPRO=0)")
}
88 changes: 88 additions & 0 deletions src/Db/feed_logic_signal.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# feed_logic_signal.template
# BASE="$(BASE)", NAME="$(NAME)", REG="$(REG)", SIZE="$(SIZE)"
# IDX="$(IDX=default)", MASK="$(MASK=default)"

# One logical signal (or many?) in an array register
#
# Entry point: "$(BASE)E_"
#
# Required Macros:
# BASE - common record prefix
# NAME - FEED device instance name
# REG - register name
# SIZE - max. register size (in elements)
#
# Optional Macros:
# IDX - index of this signal in mask register, or index of first sample if no mask register.
# MASK - Link (including 'CP MS') to signal selection mask.
# FLNK - Next record in chain after waveform update

# calculate de-mux offset and step

record(longin, "$(BASE)MASK_") {
field(INP , "$(MASK=1)")
field(PINI, "YES")
field(FLNK, "$(BASE)CALCOFF_")
}

# calculate offset of first sample
record(sub, "$(BASE)CALCOFF_") {
field(SNAM, "sub_feed_nset_bits")
field(INPA, "$(IDX=0)")
field(INPB, "$(BASE)MASK_ NPP MSI")
field(FLNK, "$(BASE)CALCSTP_")
}

# calculate total number of selected signals
record(sub, "$(BASE)CALCSTP_") {
field(SNAM, "sub_count_bits")
field(INPA, "$(BASE)MASK_ NPP MSI")
field(FLNK, "$(BASE)OFFSET")
}

record(longout, "$(BASE)OFFSET") {
field(DTYP, "FEED Signal Offset")
field(OUT , "@signal=$(BASE)")
field(OMSL, "closed_loop")
field(DOL , "$(BASE)CALCOFF_ NPP MSI")
field(FLNK, "$(BASE)STEP")
}

record(longout, "$(BASE)STEP") {
field(DTYP, "FEED Signal Step")
field(OUT , "@signal=$(BASE)")
field(OMSL, "closed_loop")
field(DOL , "$(BASE)CALCSTP_ NPP MSI")
}

# scaling factor

record(ao, "$(PREF)SCALE") {
field(DTYP, "FEED Signal Scale")
field(OUT , "@signal=$(BASE)")
field(PINI, "YES")
field(VAL , "1.0")
field(PREC, "9")
info(autosaveFields_pass0, "VAL")
}

# Signal readout

record(aai, "$(BASE)WF") {
# scan disable when this signal is not selected
field(SDIS, "$(BASE)OFFSET")
field(DISV, "-1")

field(DTYP, "FEED Register Read")
field(DESC, "$(DESC=Reg $(NAME))")

# wait=false uses cached value
field(INP , "@name=$(NAME) reg=$(REG) signal=$(BASE) wait=false")
field(FTVL, "DOUBLE")
field(NELM, "$(SIZE)")
field(TSE , "-2")

field(FLNK, "$(FLNK=)")

alias("$(BASE)E_")
}
Loading

0 comments on commit 834fa34

Please sign in to comment.