Skip to content

Commit

Permalink
Put in some meaningful files
Browse files Browse the repository at this point in the history
Mostly documentation, still not the actual schematics
  • Loading branch information
ldoolitt committed May 9, 2020
1 parent de623f2 commit c26cdda
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 1 deletion.
61 changes: 60 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,61 @@
# Zest
Zest is a FMC mezzanine board with 6 ADC channels and 2 DACs

This directory will hold the schematics and associated files (but not the layout)
for the the Zest board, an FMC-ish mezzanine board designed for LLRF and related applications.
Before its release as Open Hardware, it was known as the LCLS-II LLRF Digitizer Board, currently Rev. 1.1.
It is in gschem format; to work with it you
need [gschem](http://wiki.geda-project.org/geda:gaf) installed.
Tested on [Debian](https://www.debian.org) Jessie, Stretch, or Buster, where
you simply `apt-get install geda-gschem`.

![Image of completed board](doc/digitizer_top_x.jpg) [block diagram](doc/digitizer_block.png)

The digitizer board features:

* 8 x transformer-coupled inputs sampled at 95 MS/s (2 x AD9653)
* 2 x transformer-coupled outputs sampled at 190 MS/s (AD9781)
* Input clock source up to 3 GHz (LMK01801, no on-board oscillator)
* Extra clock divider output
* 2 x Pmod digital I/O
* Interface to FPGA via dual-LPC-FMC connectors
* 181.8 x 110 mm, 8-layer, with notch to accommodate Xilinx FMC eval boards
* 4W power dissipation

## Schematics

To get PDF versions for reference,
simply `make` in this directory. The result is digitizer_schematics.pdf.

## Artwork/Gerbers

Created by Kathy Pham at SLAC using PADS. Latest is 20170519.
The PADS design imports the netlist exported from this gschem design.
These PADS files are _not_ kept in this git repository, but
their SHA256 signatures are kept in the
[layout_20170519.sha256sum](layout_20170519.sha256sum) file in this directory.

QR-code serial-number overlay Gerbers are generated with `qr_gen.py`.

## BOM

Get a copy of the `PC-379-396-15-C02_DIGITIZER BOARD_XY.xlsx` file exported from
the PADS design reference above, and place it in this directory.

Then `make merged_xy.csv` to merge the actual orderable part numbers (kept here
in the parts.data file) and get a usable xy assembly file for fabrication.

## Plastic Cover

One corner of the digitizer (by J3, clock input) has some fragile transformers
very close to where SMA wrenches are used.
It has proved helpful to have a protective cover in place to avoid damage.
This is designed in [OpenSCAD](http://www.openscad.org/) with a process that
matches up its features with the Gerber file; see covergen.py.
The OpenSCAD source file is cover1.scad.
OpenSCAD will export an STL file which is easily 3-D printed.

## References

G. Huang, L. R. Doolittle, J. Yang, Y. Xu,
*``Low Noise Digitizer Design for LCLS-II LLRF,''* in NAPAC2016
[TUPOA40](http://accelconf.web.cern.ch/AccelConf/napac2016/papers/tupoa40.pdf).
45 changes: 45 additions & 0 deletions cover1.scad
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* Digitizer cover to protect its transformers near J3 and J20 */
/* Obsolete, see cover2c.scad */
/* See also covergen.py */

/* 5 mm from board to attached SMA connector, which still fits in */
/* the 7.9 mm cutout for the board-mount SMA connector. */
/* 7 mm from board to the beginning of the hex section. */
/* Part is 5.5 mm thick, to avoid slight interference with SMA connectors found with a 6mm thick version. */

module cover()
{
union() {
difference() {
union () {
translate([0, -39.5, 2]) cube([40.5, 39.5, 3.5]);
/* copper pad on board measures 5.8 mm dia */
translate([37.55,-36.5, 0]) cylinder(r=2.9, h=4, $fn=30);
}
union () {
/* 106.3 mil = 2.7 mm dia hole, tight clearance for 4-40 */
translate([37.55, -36.5, -1]) cylinder(r=1.5, h=8, $fn=30);
translate([ -1, -11.50, 0]) cube([10.6, 7.7, 8]); /* J3 */
translate([ -1, -26.75, 0]) cube([10.6, 7.7, 8]); /* J20 */
translate([ -1, -42.00, 0]) cube([10.6, 7.7, 8]); /* J11 */
/* TCM4-19 datasheet claims max height 4.06 mm, I measure 3.1 mm */
translate([11.3, -15.2, 0]) cube([4.2, 4, 4.0]); /* U34 */
translate([17.3, -15.2, 0]) cube([4.2, 4, 4.0]); /* T1 */
translate([11.3, -26.0, 0]) cube([4.2, 4, 4.0]); /* T1 */
translate([39, -24, 1]) mirror(v=[1, 0, 0]) linear_extrude(height = 1.5) {
text("LBNL", size = 6.2, font = "Liberation Sans");
}
translate([28, -34, 1]) mirror(v=[1, 0, 0]) linear_extrude(height = 1.5) {
text("R3", size = 6.2, font = "Liberation Sans");
}
}
}
/* these are the stubs supposed to rest on the bare board */
translate([0, -3.6, 0]) cube([ 9.5, 3.6, 2]);
translate([0, -18.2, 0]) cube([ 9.5, 3.6, 2]);
translate([0, -33.1, 0]) cube([16.0, 5.2, 2]);
translate([25.3, -6.1, 0]) cube([ 9.5, 6.1, 2]);
}
}

cover();
Binary file added doc/digitizer_block.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/digitizer_top_x.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions layout_20170519.sha256sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
6cbed80c2a59994bd1079787402c8bbd41b0e4aac596df6ae13ff0d52cd22452 PC-379-396-15-C02_DIGITIZER BOARD_GERBER.zip
41695319dbba6b2a0f070e53f15aa4e91d6cd02aab0c0f8df19309e846284ad0 PC-379-396-15-C02_DIGITIZER BOARD.pcb
755974be8790143d8e31a287eb366f4e4facb41aa9633fdf04591ec5c7711a8d PC-379-396-15-C02_DIGITIZER BOARD_XY.xlsx
e4acd72a8bf70de439603d0e74e5d18f4af4e1b64bf01f2c5adaf508655656ea PC-379-396-15-C02_DIGITIZER SA TOP.pdf
91 changes: 91 additions & 0 deletions qr_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import numpy
import qrtools
import scipy.ndimage
import os

def qrpng2pcbpoly(qrpngfilename, x0=9600, y0=3200):
im = scipy.ndimage.imread(qrpngfilename, flatten=True)
ymax, xmax = im.shape
y, x = numpy.where(im == 255)
x0 = x0 - 450
y0 = y0 + 430
step = 14
polygons = []
for ix, xvalue in enumerate(x):
x1 = x0+xvalue*step
x2 = x0+xvalue*step+step
y1 = y0-(ymax-y[ix])*step
y2 = y0-(ymax-y[ix])*step+step
polygons.append(''' Polygon("clearpoly")
(
[%.2fmil %.2fmil] [%.2fmil %.2fmil] [%.2fmil %.2fmil] [%.2fmil %.2fmil]
)''' % (x1, y1, x2, y1, x2, y2, x1, y2))
return '\n'.join(polygons)

# String length limits for Version 2 (25x25), assuming "Alphanumeric" encoding:
# level='S' 40
# level='M' 28
# level='Q' 22
def qrgen(string, level='M'):
print("qrgen: " + string)
qr = qrtools.QR(string, pixel_size=1, margin_size=2, level=level)
qr.encode(string)
return string+'.png'

def pcbstring(x, y, size, s):
return '''
Text[%3.2fmil %3.2fmil 0 %d "%s" "clearline"]''' % (x, y, size, s)

def pcbstrings(l):
return "".join([pcbstring(*ll) for ll in l])

def munge_size(l, xmax, ymax):
if l[0:7] == 'PCB["" ':
return 'PCB["" %.2fmil %.2fmil]' % (xmax, ymax)
else:
return l

def get_template(xmax=12000, ymax=10000):
with open("template.pcb", "r") as f:
template = f.read()
lines = template.split('\n')
return "\n".join([munge_size(l, xmax=xmax, ymax=ymax) for l in lines])


def pcbfile(silk, template):
return template + '''Layer(10 "top silk" "silk")
(
'''+silk+'''
)'''

def infopcb(lines, sn, x0=9600, y0=3200):
line_break = lines.split('\n')
return pcbstrings([
(x0, y0+0, 110, line_break[0]),
(x0, y0+70, 110, line_break[1]),
(x0, y0+140, 110, line_break[2]),
(x0, y0+210, 200, 'S'),
(x0, y0+320, 200, 'N'),
(x0+100, y0+180, 500, '%03d' % sn)])

def oneboard(gerber_name="a.gbr", sn=1, qr_string="TEST 1", desc_string="", x0=3900, y0=1200, level="M", template="", lines=""):
polys = qrpng2pcbpoly(qrgen(qr_string, level=level), x0=x0, y0=y0)
pcbstr = pcbfile(silk=infopcb(lines, sn, x0=x0, y0=y0)+polys, template=template)
pcb_name = 'test'
with open(pcb_name+'.pcb', 'w') as f:
f.write(pcbstr)
f.close()
os.system('pcb -x gerber %s.pcb && mv %s.%s.gbr %s' % (pcb_name, pcb_name, 'topsilk', gerber_name))


if __name__ == "__main__":
setup = [3900, 7450, 'qr_sn_%03d.gbr', 'LBNL DIGITIZER V1.1 SN %03d', 'M', 'LBNL Digitizer\nLCLS-II LLRF\nRevision 1.1']
# setup = [10000, 5360, 'qr_dn_sn_%03d.gbr', 'FNAL DOWNCVT REV C SN %03d', 'M', 'FNAL DOWNCVT\nLCLS-II LLRF\nRev C']
# setup = [6900, 2300, 'qr_up_sn_%03d.gbr', 'FNAL UPCVT REV C SN %03d', 'M', 'FNAL UPCVT\nLCLS-II LLRF\nRev C']
x0, y0, gerber_base, qr_base, level, lines = setup
template = get_template(xmax=x0+1000, ymax=y0+1000)
for sn in range(32, 57):
gerber_name = gerber_base % sn
qr_string = qr_base % sn
desc_string = ''
oneboard(gerber_name=gerber_name, sn=sn, qr_string=qr_string, desc_string=desc_string, x0=x0, y0=0, level=level, template=template, lines=lines)

0 comments on commit c26cdda

Please sign in to comment.