In the .kicad_sch file (made by KiCad v5.99) the information about what net is connected to what pin of the FPGA is not so easy to obtain. First you need to figure out there the 5 parts of the FPGA are placed in the schematic from this part of the file:
(symbol (lib_id "FPGA_Lattice:ICE40UP5K-SG48ITR") (at 165.1 34.29 0) (unit 4)
(in_bom yes) (on_board yes)
(uuid "e8966ff8-798c-4f44-abf7-133b83162563")
(property "Reference" "U1" (id 0) (at 171.45 33.02 0)
(effects (font (size 1.27 1.27)) (justify left))
)
(property "Value" "ICE40UP5K-SG48ITR" (id 1) (at 171.45 34.29 0)
(effects (font (size 1.27 1.27)) (justify left))
)
(property "Footprint" "Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm" (id 2) (at 165.1 68.58 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.latticesemi.com/Products/FPGAandCPLD/iCE40Ultra" (id 3) (at 154.94 8.89 0)
(effects (font (size 1.27 1.27)) hide)
)
)
(symbol (lib_id "FPGA_Lattice:ICE40UP5K-SG48ITR") (at 142.24 64.77 0) (unit 3)
(in_bom yes) (on_board yes)
(uuid "e34fcc4f-fe4b-4392-8980-b86d88bc0732")
(property "Reference" "U1" (id 0) (at 151.13 60.96 0)
(effects (font (size 1.27 1.27)) (justify left))
)
(property "Value" "ICE40UP5K-SG48ITR" (id 1) (at 151.13 63.5 0)
(effects (font (size 1.27 1.27)) (justify left))
)
(property "Footprint" "Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm" (id 2) (at 142.24 99.06 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.latticesemi.com/Products/FPGAandCPLD/iCE40Ultra" (id 3) (at 132.08 39.37 0)
(effects (font (size 1.27 1.27)) hide)
)
)
(symbol (lib_id "FPGA_Lattice:ICE40UP5K-SG48ITR") (at 68.58 58.42 0) (unit 1)
(in_bom yes) (on_board yes)
(uuid "1b277448-4846-40e8-8836-2f8c2575715e")
(property "Reference" "U1" (id 0) (at 77.47 55.88 0)
(effects (font (size 1.27 1.27)) (justify left))
)
(property "Value" "ICE40UP5K-SG48ITR" (id 1) (at 77.47 58.42 0)
(effects (font (size 1.27 1.27)) (justify left))
)
(property "Footprint" "Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm" (id 2) (at 68.58 92.71 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.latticesemi.com/Products/FPGAandCPLD/iCE40Ultra" (id 3) (at 58.42 33.02 0)
(effects (font (size 1.27 1.27)) hide)
)
)
(symbol (lib_id "FPGA_Lattice:ICE40UP5K-SG48ITR") (at 109.22 59.69 0) (unit 2)
(in_bom yes) (on_board yes)
(uuid "a664fa94-5c1a-4e98-9fb4-8a989926cba8")
(property "Reference" "U1" (id 0) (at 109.22 87.63 0))
(property "Value" "ICE40UP5K-SG48ITR" (id 1) (at 109.22 90.17 0))
(property "Footprint" "Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm" (id 2) (at 109.22 93.98 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.latticesemi.com/Products/FPGAandCPLD/iCE40Ultra" (id 3) (at 99.06 34.29 0)
(effects (font (size 1.27 1.27)) hide)
)
)
That gives you the designator (U1), the value (ICE40UP5K-SG48ITR), and 4 locations.
Next this snipped tells you where all the pins of the FPGA are, relative to the 4 locations obtained above:
(symbol "FPGA_Lattice:ICE40UP5K-SG48ITR" (in_bom yes) (on_board yes)
(property "Reference" "U" (id 0) (at -8.89 -29.21 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "ICE40UP5K-SG48ITR" (id 1) (at 0 -31.75 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "Package_DFN_QFN:QFN-48-1EP_7x7mm_P0.5mm_EP5.6x5.6mm" (id 2) (at 0 -34.29 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.latticesemi.com/Products/FPGAandCPLD/iCE40Ultra" (id 3) (at -10.16 25.4 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_locked" "" (id 4) (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(property "ki_keywords" "FPGA programmable logic" (id 5) (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_description" "iCE40 UltraPlus FPGA, 5280 LUTs, 1.2V, 48-pin QFN" (id 6) (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_fp_filters" "QFN*7x7mm*P0.5mm*EP5.6x5.6mm*" (id 7) (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "ICE40UP5K-SG48ITR_1_1"
(rectangle (start -7.62 25.4) (end 7.62 -27.94)
(stroke (width 0.254)) (fill (type background))
)
(pin bidirectional line (at -10.16 12.7 0) (length 2.54)
(name "IOT_37a" (effects (font (size 1.27 1.27))))
(number "23" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at -10.16 15.24 0) (length 2.54)
(name "IOT_36b" (effects (font (size 1.27 1.27))))
(number "25" (effects (font (size 1.27 1.27))))
)
After figuring out the real position of the pins of the FPGA, you then have to see if there’s a line connected to those pins, and if there’s lines connected to those lines from this snippet:
(wire (pts (xy 15.24 45.72) (xy 15.24 74.93))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 26.67 55.88) (xy 26.67 74.93))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 38.1 66.04) (xy 38.1 74.93))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 46.99 71.12) (xy 58.42 71.12))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 46.99 74.93) (xy 46.99 71.12))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 58.42 45.72) (xy 15.24 45.72))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 58.42 55.88) (xy 26.67 55.88))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 58.42 66.04) (xy 38.1 66.04))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
(wire (pts (xy 165.1 44.45) (xy 165.1 49.53))
(stroke (width 0) (type solid) (color 0 0 0 0))
)
And finally you need to see if the location of the lables is on the location of the wires from this snipped:
(label "FPGA_LED1" (at 30.48 45.72 180)
(effects (font (size 1.27 1.27)) (justify right bottom))
)
(label "FPGA_LED2" (at 38.1 55.88 180)
(effects (font (size 1.27 1.27)) (justify right bottom))
)
(label "FPGA_LED3" (at 48.26 66.04 180)
(effects (font (size 1.27 1.27)) (justify right bottom))
)
(label "FPGA_LED4" (at 57.15 71.12 180)
(effects (font (size 1.27 1.27)) (justify right bottom))
)
So this approach basically forces you to duplicate all the code that’s already in the KiCad netlist generator. I think it’s much more efficient (in terms of development effort) to ask eeschema to generate the netlist, and then parse that netlist file. This way Eeschema also deals with the differences between schematics drawn in v5 and v6, so the python script does not have to know about that.
This does raise the question how eeschema can be called from python, so it reads the schematic, and generate a netlist file.
This also raise the question if the netlist from eeschema v5 and from eeschema v6 are the same.
Maybe it’s better to make a new netlist generator that can be added to eeschema (eeschema -> file->export->export netlist->add generator). Where can I find examples how to do that?