I am playing around with SKiDL and run into some 'issues'. I was wondering if there is anybody out there that also does programmatic net list generation and has figured out these issues.
Must be said that I am new to Python so there may be some obvious solutions that I did not think of.
- How do you structure your code in order to keep it readable?
I know you can make functions and for loops etc. but most code turns out to be a sequential list of connecting parts... - How do you manage part naming (annotation)?
I have adopted having a Bom file that uses arrays for each type to pre-create all parts. This will name them in order. Then connect them with something like R[2][1] += D[2].A - this would connect R2 with the Anode (alias) of D2. - How do you validate correctness?
After you have coded your net list, how do you know for sure that its all good - besides running rules. Simply start laying out the PCB and run into the errors there? (yak!)
I am working from an existing schematic (Harlequin one of the ZX Spectrum clones) which is fairly large - not trivial in any case.
Thoughts?
Edit: code examples.
# libraries
device = 'device'
# footprints
fpR = 'Resistor_SMD:R_0805_2012Metric'
fpC = 'Capacitor_SMD:C_0805_2012Metric'
fpD = 'Diode_SMD:R_0805_2012Metric'
R = [{}]
C = [{}]
D = [{}]
# zero indexes are never used
for r in range(65):
R.append(Part(device, 'R' , footprint=fpR))
for c in range(45):
C.append(Part(device, 'C', footprint=fpC))
for d in range(10):
D.append(Part(device, 'D', footprint=fpD))
C[1].value = '10uF'
C[3].value = '10uF'
C[27].value = '10uF'
C[2].value = '27pF'
C[4].value = '27pF'
C[25].value = '10pF'
...
# global nets
vcc = Net('VCC')
gnd = Net('GND')
busD = Bus('D', 8)
busA = Bus('A', 16)
# z80 signals
clk = Net('CLK')
rst = Net('RST')
rd = Net('RD')
wr = Net('WR')
mreq = Net('MREQ')
iorq = Net('IORQ')
rfsh = Net('RFSH')
m1 = Net('M1')
nmi = Net('NMI')
int = Net('INT')
busrq = Net('BUSRQ')
busack = Net('BUSACK')
wait = Net('WAIT')
halt = Net('HALT')
# CPU and memory
z80 = U[1]
z80['VCC'] += vcc
z80['GND'] += gnd
z80['D[0:7]'] += busD
z80['A[0:15]'] += busA
z80['~RESET'] += rst
z80['~CLK'] += clk
z80['~IORQ'] += iorq
z80['~RD'] += rd
z80['~WR'] += wr
z80['~BUSRQ'] += busrq
z80['~BUSACK'] += busack
z80['~WAIT'] += wait
z80['~INT'] += int
z80['~NMI'] += nmi
z80['~M1'] += m1
z80['~HALT'] += halt
z80['~RFSH'] += rfsh
z80['~MREQ'] += mreq
rom = U[2]
rom['VCC'] += vcc
rom['GND'] += gnd
rom['D[0:7]'] += busD
rom['A[0:13]'] += busA['A[0:13]']
#rom['A[14:15'] += busA
#rom['~WE'] += vcc
rom['~OE'] += rd
rom['~CE'] += Net.fetch('ROMCS')
ram = U[3]
ram['VCC'] += vcc
ram['GND'] += gnd
ram['Q[0:7]'] += busD
ram['A[0:13]'] += busA['A[0:13]']
ram['A[14]'] += Net.fetch('RA14')
ram['A[15]'] += Net.fetch('RA15')
ram['~OE'] += rd
ram['~WE'] += wr
ram['~CS1'] += Net.fetch('RAM')
ram['CS2'] += busA[15]
This sort of follows up on this discussion: https://www.eevblog.com/forum/eda/no-schematic-pcb-workflow/ (https://www.eevblog.com/forum/eda/no-schematic-pcb-workflow/)
After that, I actually got to design my own tool (I found Skidl interesting but 1/ didn't like Python much and 2/ thought there were things lacking for it to be really usable, at least by me). My tool is based on Lua and I codenamed it "Lucid" (loosely for Lua-based Circuit Design) for the time being. I'm not open-sourcing it for now (might change later on), so I'm sorry I can't share it yet. I designed a board with it (an isolated SWD/JTAG probe with USB-C) and used KiCad to route it. It turned out pretty good. Just a quick picture attached (yes there is a manual correction as can be seen, but it was due to some detail I overlooked while designing it, and has actually nothing to do with my tool itself, I would have made the same mistake with a schematic).
So anyway, I can give you a couple hints since I have this experience, but I have none with Skidl so I can't comment on what can or can't be done with it.
1. The key IMO with a schematic-less design is (as we also discussed in the above thread) to use hierarchy as much as you can, so you can deal with sub-blocks that you can then instantiate and connect in higher-level blocks. That keeps the design tidy and readable, and keeps the number of parts in each sub-block manageable. You can of course also use any feature of the underlying language (Python in your case), but the key really is to segment your design in small blocks. If some blocks are similar but with small variations (component value or otherwise), you can of course write functions to generate those blocks with parameters. Pretty handy. Also, if you can, when dealing with identical parts (like a series of 100nF decoupling caps, make clones of one rather than create each one (which is very annoying and error-prone). Such as in this example you gave:
C[1].value = '10uF'
C[3].value = '10uF'
C[27].value = '10uF'
I'd suggest creating a 10uF capacitor first, and then making copies for all the identical ones (it can probably be done with Skidl/Python).
2. Not sure about how Skidl works here, but annotation was certainly a big concern of mine (as again discussed in the previous thread). So with my tool, annotation is automatic. What it means is that you name the parts in each block as you see fit (with identifiers that make sense to you, a bit like naming variables), and while analyzing the circuit, my tool will automatically generate conventional references (such as R1, C2, U5...) from the internal, hierarchical part/block names, and generate a "dictionary" file (internal names vs. annotated references in text files). I find it actually pretty usable. Finding a reference later on is dead easy (especially since the dictionaries are sorted), and since the internal names are hierarchical, it's easy to spot where a given reference lies in your circuit. I'm not sure Skidl can do that, and that would be the most annoying thing IMO.
3. Good question, but how do you validate correctness with a schematic? The key here again is to make your design "readable", since it's much harder to figure out a circuit from a textual representation rather than from a graphical one. So keep it very hierarchical and validate it by relatively small sub-blocks. I don't know whether Skidl has ERC (I think so), I added that in my own tool and it's invaluable: you can definitely not do without ERC when going schematic-less. Much too easy to forget connections for instance, so ERC should definitely give you the unconnected ports. To ease verification (and communication with others), I've started working on automatic generation of some kind of block diagrams. Not that easy. Something like the rtl-to-schematic tools you can find in many FPGA IDEs would be handy, but it's definitely not so simple to write and get something visually useful. So I'm not there yet.