-
Notifications
You must be signed in to change notification settings - Fork 18
PCB Design
Routing PCB traces can be very time consuming if you have to construct it from individual ractangles. To simplify the process, EMerge comes with a PCBLayouter that drastically simplifies the process. This page will guide you through the basics of the layouter.
A PCBLayouter can be created from the .geo submodule.
import emerge as em
mm = 0.001
m = em.Simulation3D('pcb sim')
pcbl = em.geo.PCBLayouter(1.54, mm, material=em.lib.FR4)The PCBLayouter itself is not a geometry object. It is simply an interface to create them efficiently. The PCBLayouter heavily ultilizes a so called method chaining philosophy. Method chaining involves around methods returning itself or different classes so that a next instruction can be directly added. For the PCBLayouter, traces can be created using the .new() method. This will return a StripPath object which represents a single path from start to finish.
To simplify the typing, all dimensions in the PCBLayouter class including the thickness are provided in a specific unit. In the example above, we defined the unit as mm. Thus, all numbers plugged in will be measured in millimeters. Lets start by showing how to make the following stripline design:
<---5mm--> <----7mm--->
^ +-------------+ +---------------+
2mm | | | |
v +--------+ | | +----------+
| | ^ | |
| | 4mm | |
| | v | |
| +-------------+ |
| |
+-----------------------+
<-----10mm---->
This trace does the following:
- Start with a width of 2mm and move 5mm forwards
- make a 90 degree turn right
- Go 4mm forward
- make a 90 degree turn left
- go 10 mm forward
- make a 90 degree turn left
- Go 4mm forward
- make a 90 degree turn right
- Go 7mm forward
To create this trace in EMerge you can simply do the following:
pcb.new(0,0,2,(1,0)).straight(5).turn(90).straight(4).turn(-90).straight(10).turn(-90).straight(4).turn(90).straight(7)With every call to .straight() or .turn() the same StripPath object is returned after which you can call a new routing instruction. There are many instructions available
-
.straight()= Go a certain distance straight -
.turn()= Make a turn (positive is clockwise) -
.via()= Add a via and start a new trace at a different depth -
.taper()= Add a straight section tapering to a different width -
.jump()= Stop the current trace and continue somewhere else. (Useful for coupled line filters for example. -
.macro()= Use the EMerge routing macro language to define a path -
.to()= Auto route towards a certain destination -
.stub()= Add a finite length straight section branching out from the current point -
.store()= Remember the current point for later use. -
.split()= Remember the current point and go along a different route, -
.merge()= Move back to the last call of.split()For many of these commands, the width can be changed in order to make things like stepped impedance filter.
In order to turn the paths into actual geometries, you have to use the .compile_paths() method on the PCBLayouter. This will turn all instructions into actual polygons. Each individual path will be rendered to its own geometry unless you turn the optional merge=True argument on.
traces = pcbl.compile_paths()After traces have been define, it is possible to define the bounds for the PCB. The bounds will be based by the bounding box of the PCB in the XY-plane. You can add margins to keep the bounding box some distance away from the traces.
pcbl.define_bounds(left=5*mm) #Determines the bounds with a 5mm margin on the leftAfter defining the bounds, you can add volumes.
To create the dielectric, use the .gen_pcb() method. Optional arguments allow you to split the volume into different sub-volumes for each z-height detected. The z-height is defined uniquely for each strip path. The material will be automatically assigned
pcb = pcbl.gen_pcb(merge=True) #returns one geometryYou can create air above your domain up to a certain height using the .gen_air() function
air = pcbl.gen_air(10*mm) #adds 10mm of air above your PCBTo create a 2D plane, simply use the .plane() method. This can be used to create random rectangles or planes that occupy the entire bounds. The PCBLayouter has an optional argument layers that allows you to define some amount of copper layers (min 2). The pcbl.z(1) will allow you to get the z-height of each layer.
pcbl = em.geo.PCBLayouter(1.5, mm, layers=3)
ground = pcbl.plane(z=pcbl.z(1))
middle = pcbl.plane(z=pcbl.z(2))You can automatically create port rectangles to use for lumped ports (inside domain) or modal ports (boundary of domain) as following:
port_face_1 = pcbl.modal_port(pcbl.load('my_point'))
lumped_port_face = pcbl.lumped_port(pcbl.load('my_lumped_port_point'))These .load('yourname') commands will take the positions you can identify by storing a point during routing. For a simple simulation of a jump in impedance you could do the following.
import emerge as em
mm = 0.001
w1 = 1.2 #mm
w2 = 1.8 #mm
L = 20 #mm
m = em.Simulation3D('impjump')
pcbl = em.geo.PCBLayouter(1.5, mm)
pcbl.new(0,0,w1,(1,0)).store('p1').straight(L).straight(L,w2).store('p2')
modal_port1 = pcbl.modal_port(pcbl.load('p1'))
lumped_port = pcbl.lumped_port(pcbl.load('p2'))
# more code
m.mw.bc.LumpedPort(lumped_port, Z0=50)If you pass a lumped_port rectangle to the LumpedPort boundary-condition constructor, the width, height and orientation information will be automatically passed on.
Vias generated during the layouting process have to be created as specific objects using the .generate_vias() method
all_vias = pcbl.generate_vias(merge=True)
model.mw.be.PEC(all_vias.outside())