diff --git a/.gitignore b/.gitignore index 2e1d42f6..1be56903 100644 --- a/.gitignore +++ b/.gitignore @@ -382,4 +382,7 @@ Software/LogicAnalyzer/PythonInitLog.txt Packages/ Software/Packages/ Software/Merged/ - +logicanalyzer.wiki/ +Old/ +Electronics/LogicAnalyzer/LogicAnalyzerPro*/ +Electronics/LogicAnalyzer/LogicAnalyzerV2/packages/ \ No newline at end of file diff --git a/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-10-15_181430.zip b/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-07_123216.zip similarity index 59% rename from Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-10-15_181430.zip rename to Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-07_123216.zip index 5a779ff9..052fec6a 100644 Binary files a/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-10-15_181430.zip and b/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-07_123216.zip differ diff --git a/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-09-26_214040.zip b/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-11_152531.zip similarity index 52% rename from Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-09-26_214040.zip rename to Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-11_152531.zip index 42691ad8..052fec6a 100644 Binary files a/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-09-26_214040.zip and b/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-11_152531.zip differ diff --git a/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-09-26_235900.zip b/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-25_004806.zip similarity index 52% rename from Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-09-26_235900.zip rename to Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-25_004806.zip index 42691ad8..052fec6a 100644 Binary files a/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2024-09-26_235900.zip and b/Electronics/LogicAnalyzer/LogicAnalyzerV2/designs/jitx-design/kicad/jitx-design-backups/jitx-design-2025-06-25_004806.zip differ diff --git a/Electronics/LogicAnalyzer/tmp/components/RP2350B-QFN80.stanza b/Electronics/LogicAnalyzer/tmp/components/RP2350B-QFN80.stanza new file mode 100644 index 00000000..7d7c285b --- /dev/null +++ b/Electronics/LogicAnalyzer/tmp/components/RP2350B-QFN80.stanza @@ -0,0 +1,518 @@ +#use-added-syntax(jitx) +defpackage LogicAnalyzerPro/components/UNKNOWN/RP2350B-QFN80 : + import core + import jitx + import jitx/commands + import jitx/parts + import jsl/symbols/SymbolDefn with: (only => (create-symbol)) + import jsl/symbols/box-symbol with: (only => (assign-symbols, assign-symbol, BoxSymbol)) + + +public pcb-symbol RP2350B-QFN80 : + pin GPIO4 at Point(31.750, 40.640) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin DVDD[2] at Point(-21.590, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO12 at Point(31.750, 20.320) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO13 at Point(31.750, 17.780) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO14 at Point(31.750, 15.240) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO15 at Point(31.750, 12.700) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[5] at Point(6.350, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO16 at Point(31.750, 10.160) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO17 at Point(31.750, 7.620) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO18 at Point(31.750, 5.080) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO19 at Point(31.750, 2.540) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO5 at Point(31.750, 38.100) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO20 at Point(31.750, 0.0) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO21 at Point(31.750, -2.540) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO22 at Point(31.750, -5.080) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO23 at Point(31.750, -7.620) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[4] at Point(3.810, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO24 at Point(31.750, -10.160) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO25 at Point(31.750, -12.700) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO26 at Point(31.750, -15.240) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO27 at Point(31.750, -17.780) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[6] at Point(8.890, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO6 at Point(31.750, 35.560) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin XIN at Point(-33.020, 15.240) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin XOUT at Point(-33.020, 10.160) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin DVDD[1] at Point(-24.130, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin SWCLK at Point(-33.020, -12.700) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin SWDIO at Point(-33.020, -15.240) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin RUN at Point(-33.020, -2.540) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO28 at Point(31.750, -20.320) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO29 at Point(31.750, -22.860) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO30 at Point(31.750, -25.400) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO31 at Point(31.750, -27.940) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO7 at Point(31.750, 33.020) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO32 at Point(31.750, -30.480) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[3] at Point(1.270, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO33 at Point(31.750, -33.020) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO34 at Point(31.750, -35.560) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO35 at Point(31.750, -38.100) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO36 at Point(31.750, -40.640) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO37 at Point(31.750, -43.180) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO38 at Point(31.750, -45.720) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO39 at Point(31.750, -48.260) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO40_ADC0 at Point(31.750, -53.340) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[7] at Point(11.430, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[2] at Point(-1.270, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin DVDD[0] at Point(-26.670, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO41_ADC1 at Point(31.750, -55.880) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO42_ADC2 at Point(31.750, -58.420) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO43_ADC3 at Point(31.750, -60.960) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO44_ADC4 at Point(31.750, -63.500) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO45_ADC5 at Point(31.750, -66.040) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO46_ADC6 at Point(31.750, -68.580) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO47_ADC7 at Point(31.750, -71.120) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin ADC_AVDD at Point(19.050, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO8 at Point(31.750, 30.480) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[1] at Point(-3.810, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_AVDD at Point(-33.020, 57.150) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_PGND at Point(-33.020, 46.990) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_LX at Point(-33.020, 49.530) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_VIN at Point(-33.020, 54.610) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_FB at Point(-33.020, 52.070) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin USB_DM at Point(31.750, 58.420) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin USB_DP at Point(31.750, 60.960) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin USB_OTP_VDD at Point(15.240, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_IOVDD at Point(-13.970, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO9 at Point(31.750, 27.940) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD3 at Point(-33.020, 29.210) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SCLK at Point(-33.020, 25.400) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD0 at Point(-33.020, 36.830) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD2 at Point(-33.020, 31.750) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD1 at Point(-33.020, 34.290) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SS at Point(-33.020, 40.640) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[0] at Point(-6.350, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO0 at Point(31.750, 50.800) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO1 at Point(31.750, 48.260) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO2 at Point(31.750, 45.720) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO10 at Point(31.750, 25.400) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO3 at Point(31.750, 43.180) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO11 at Point(31.750, 22.860) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GND at Point(0.0, -74.930) with : + direction = Down + length = 2.540 + number-size = 1.270 + name-size = 1.270 + + draw("foreground") = Text(">REF", 1.27, W, loc(2.194, -77.470)) + draw("foreground") = Text(">VALUE", 1.27, W, loc(2.194, -80.010)) + draw("foreground") = Text("Footprint: RP2350_KiCad:RP2350B_QFN-80_EP_10.573x10.573_Pitch0.4mm", 1.27, C, loc(-6.350, 45.720), "", TrueTypeFont, true, false) + draw("foreground") = Text("Datasheet: ", 1.27, C, loc(-6.350, 45.720), "", TrueTypeFont, true, false) + draw("foreground") = Text("Description: ", 1.27, C, loc(-1.270, 41.910), "", TrueTypeFont, true, false) + draw("none") = Rectangle(64.770, 140.970, loc(-0.635, -4.445)) + +public pcb-component component : + description = "" + mpn = "RP2350B-QFN80" + datasheet = "" + reference-prefix = "U1" + pin-properties : + [pin:Ref | pads:Ref ... | side:Dir | electrical-type:String] + [GPIO4 | p[1] | Right | "Bidirectional"] + [DVDD[2] | p[10] | Up | "PowerIn"] + [GPIO12 | p[11] | Right | "Bidirectional"] + [GPIO13 | p[12] | Right | "Bidirectional"] + [GPIO14 | p[13] | Right | "Bidirectional"] + [GPIO15 | p[14] | Right | "Bidirectional"] + [IOVDD[5] | p[15] | Up | "PowerIn"] + [GPIO16 | p[16] | Right | "Bidirectional"] + [GPIO17 | p[17] | Right | "Bidirectional"] + [GPIO18 | p[18] | Right | "Bidirectional"] + [GPIO19 | p[19] | Right | "Bidirectional"] + [GPIO5 | p[2] | Right | "Bidirectional"] + [GPIO20 | p[20] | Right | "Bidirectional"] + [GPIO21 | p[21] | Right | "Bidirectional"] + [GPIO22 | p[22] | Right | "Bidirectional"] + [GPIO23 | p[23] | Right | "Bidirectional"] + [IOVDD[4] | p[24] | Up | "PowerIn"] + [GPIO24 | p[25] | Right | "Bidirectional"] + [GPIO25 | p[26] | Right | "Bidirectional"] + [GPIO26 | p[27] | Right | "Bidirectional"] + [GPIO27 | p[28] | Right | "Bidirectional"] + [IOVDD[6] | p[29] | Up | "PowerIn"] + [GPIO6 | p[3] | Right | "Bidirectional"] + [XIN | p[30] | Left | "Input"] + [XOUT | p[31] | Left | "Output"] + [DVDD[1] | p[32] | Up | "PowerIn"] + [SWCLK | p[33] | Left | "Bidirectional"] + [SWDIO | p[34] | Left | "Bidirectional"] + [RUN | p[35] | Left | "Input"] + [GPIO28 | p[36] | Right | "Bidirectional"] + [GPIO29 | p[37] | Right | "Bidirectional"] + [GPIO30 | p[38] | Right | "Bidirectional"] + [GPIO31 | p[39] | Right | "Bidirectional"] + [GPIO7 | p[4] | Right | "Bidirectional"] + [GPIO32 | p[40] | Right | "Bidirectional"] + [IOVDD[3] | p[41] | Up | "PowerIn"] + [GPIO33 | p[42] | Right | "Bidirectional"] + [GPIO34 | p[43] | Right | "Bidirectional"] + [GPIO35 | p[44] | Right | "Bidirectional"] + [GPIO36 | p[45] | Right | "Bidirectional"] + [GPIO37 | p[46] | Right | "Bidirectional"] + [GPIO38 | p[47] | Right | "Bidirectional"] + [GPIO39 | p[48] | Right | "Bidirectional"] + [GPIO40_ADC0 | p[49] | Right | "Bidirectional"] + [IOVDD[7] | p[5] | Up | "PowerIn"] + [IOVDD[2] | p[50] | Up | "PowerIn"] + [DVDD[0] | p[51] | Up | "PowerIn"] + [GPIO41_ADC1 | p[52] | Right | "Bidirectional"] + [GPIO42_ADC2 | p[53] | Right | "Bidirectional"] + [GPIO43_ADC3 | p[54] | Right | "Bidirectional"] + [GPIO44_ADC4 | p[55] | Right | "Bidirectional"] + [GPIO45_ADC5 | p[56] | Right | "Bidirectional"] + [GPIO46_ADC6 | p[57] | Right | "Bidirectional"] + [GPIO47_ADC7 | p[58] | Right | "Bidirectional"] + [ADC_AVDD | p[59] | Up | "PowerIn"] + [GPIO8 | p[6] | Right | "Bidirectional"] + [IOVDD[1] | p[60] | Up | "PowerIn"] + [VREG_AVDD | p[61] | Left | "PowerIn"] + [VREG_PGND | p[62] | Left | "Passive"] + [VREG_LX | p[63] | Left | "PowerOut"] + [VREG_VIN | p[64] | Left | "PowerIn"] + [VREG_FB | p[65] | Left | "PowerIn"] + [USB_DM | p[66] | Right | "Bidirectional"] + [USB_DP | p[67] | Right | "Bidirectional"] + [USB_OTP_VDD | p[68] | Up | "PowerIn"] + [QSPI_IOVDD | p[69] | Up | "Bidirectional"] + [GPIO9 | p[7] | Right | "Bidirectional"] + [QSPI_SD3 | p[70] | Left | "Bidirectional"] + [QSPI_SCLK | p[71] | Left | "Bidirectional"] + [QSPI_SD0 | p[72] | Left | "Bidirectional"] + [QSPI_SD2 | p[73] | Left | "Bidirectional"] + [QSPI_SD1 | p[74] | Left | "Bidirectional"] + [QSPI_SS | p[75] | Left | "Bidirectional"] + [IOVDD[0] | p[76] | Up | "PowerIn"] + [GPIO0 | p[77] | Right | "Bidirectional"] + [GPIO1 | p[78] | Right | "Bidirectional"] + [GPIO2 | p[79] | Right | "Bidirectional"] + [GPIO10 | p[8] | Right | "Bidirectional"] + [GPIO3 | p[80] | Right | "Bidirectional"] + [GPIO11 | p[9] | Right | "Bidirectional"] + [GND | p[89] p[88] p[87] p[86] p[85] p[84] p[83] p[82] p[81] p[0] | Down | "Passive"] + + assign-landpattern(RP2350B/RP2350B_QFN-80_EP_10_573x10_573_Pitch0_4mm) + assign-symbol(RP2350B/RP2350B-QFN80) + + property(self.Value) = "RP2350B-QFN80" + diff --git a/Electronics/LogicAnalyzer/tmp/components/RP2350B/RP2350B-QFN80.stanza b/Electronics/LogicAnalyzer/tmp/components/RP2350B/RP2350B-QFN80.stanza new file mode 100644 index 00000000..25dd4628 --- /dev/null +++ b/Electronics/LogicAnalyzer/tmp/components/RP2350B/RP2350B-QFN80.stanza @@ -0,0 +1,417 @@ +#use-added-syntax(jitx) +defpackage LogicAnalyzerPro/symbols/RP2350B-QFN80 : + import core + import jitx + import jitx/commands + import jitx/parts + + +public pcb-symbol RP2350B-QFN80 : + pin GPIO4 at Point(31.750, 40.640) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin DVDD[2] at Point(-21.590, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO12 at Point(31.750, 20.320) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO13 at Point(31.750, 17.780) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO14 at Point(31.750, 15.240) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO15 at Point(31.750, 12.700) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[5] at Point(6.350, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO16 at Point(31.750, 10.160) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO17 at Point(31.750, 7.620) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO18 at Point(31.750, 5.080) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO19 at Point(31.750, 2.540) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO5 at Point(31.750, 38.100) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO20 at Point(31.750, 0.0) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO21 at Point(31.750, -2.540) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO22 at Point(31.750, -5.080) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO23 at Point(31.750, -7.620) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[4] at Point(3.810, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO24 at Point(31.750, -10.160) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO25 at Point(31.750, -12.700) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO26 at Point(31.750, -15.240) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO27 at Point(31.750, -17.780) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[6] at Point(8.890, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO6 at Point(31.750, 35.560) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin XIN at Point(-33.020, 15.240) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin XOUT at Point(-33.020, 10.160) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin DVDD[1] at Point(-24.130, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin SWCLK at Point(-33.020, -12.700) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin SWDIO at Point(-33.020, -15.240) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin RUN at Point(-33.020, -2.540) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO28 at Point(31.750, -20.320) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO29 at Point(31.750, -22.860) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO30 at Point(31.750, -25.400) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO31 at Point(31.750, -27.940) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO7 at Point(31.750, 33.020) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO32 at Point(31.750, -30.480) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[3] at Point(1.270, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO33 at Point(31.750, -33.020) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO34 at Point(31.750, -35.560) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO35 at Point(31.750, -38.100) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO36 at Point(31.750, -40.640) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO37 at Point(31.750, -43.180) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO38 at Point(31.750, -45.720) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO39 at Point(31.750, -48.260) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO40_ADC0 at Point(31.750, -53.340) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[7] at Point(11.430, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[2] at Point(-1.270, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin DVDD[0] at Point(-26.670, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO41_ADC1 at Point(31.750, -55.880) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO42_ADC2 at Point(31.750, -58.420) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO43_ADC3 at Point(31.750, -60.960) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO44_ADC4 at Point(31.750, -63.500) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO45_ADC5 at Point(31.750, -66.040) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO46_ADC6 at Point(31.750, -68.580) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO47_ADC7 at Point(31.750, -71.120) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin ADC_AVDD at Point(19.050, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO8 at Point(31.750, 30.480) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[1] at Point(-3.810, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_AVDD at Point(-33.020, 57.150) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_PGND at Point(-33.020, 46.990) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_LX at Point(-33.020, 49.530) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_VIN at Point(-33.020, 54.610) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin VREG_FB at Point(-33.020, 52.070) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin USB_DM at Point(31.750, 58.420) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin USB_DP at Point(31.750, 60.960) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin USB_OTP_VDD at Point(15.240, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_IOVDD at Point(-13.970, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO9 at Point(31.750, 27.940) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD3 at Point(-33.020, 29.210) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SCLK at Point(-33.020, 25.400) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD0 at Point(-33.020, 36.830) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD2 at Point(-33.020, 31.750) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SD1 at Point(-33.020, 34.290) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin QSPI_SS at Point(-33.020, 40.640) with : + direction = Left + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin IOVDD[0] at Point(-6.350, 66.040) with : + direction = Up + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO0 at Point(31.750, 50.800) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO1 at Point(31.750, 48.260) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO2 at Point(31.750, 45.720) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO10 at Point(31.750, 25.400) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO3 at Point(31.750, 43.180) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GPIO11 at Point(31.750, 22.860) with : + direction = Right + length = 2.540 + number-size = 1.270 + name-size = 1.270 + pin GND at Point(0.0, -74.930) with : + direction = Down + length = 2.540 + number-size = 1.270 + name-size = 1.270 + + draw("none") = Rectangle(64.770, 140.970, loc(-0.635, -4.445)) + diff --git a/Electronics/LogicAnalyzer/tmp/components/RP2350B/RP2350B_QFN-80_EP_10_573x10_573_Pitch0_4mm.stanza b/Electronics/LogicAnalyzer/tmp/components/RP2350B/RP2350B_QFN-80_EP_10_573x10_573_Pitch0_4mm.stanza new file mode 100644 index 00000000..c66ec618 --- /dev/null +++ b/Electronics/LogicAnalyzer/tmp/components/RP2350B/RP2350B_QFN-80_EP_10_573x10_573_Pitch0_4mm.stanza @@ -0,0 +1,153 @@ +#use-added-syntax(jitx) +defpackage LogicAnalyzerPro/landpatterns/RP2350B_QFN-80_EP_10_573x10_573_Pitch0_4mm : + import core + import jitx + import jitx/commands + import jitx/parts + + +pcb-pad circle-th-pad : + type = TH + shape = Circle(0.212) + layer(SolderMask(Top)) = Circle(0.212) + layer(SolderMask(Bottom)) = Circle(0.212) + layer(Cutout()) = Circle(0.106) + +pcb-pad rect-smd-pad-1 : + type = SMD + shape = Rectangle(0.220, 0.780) + layer(Paste(Top)) = Rectangle(0.220, 0.780) + layer(SolderMask(Top)) = Rectangle(0.220, 0.780) + +pcb-pad rect-smd-pad-2 : + type = SMD + shape = Rectangle(3.400, 3.400) + layer(SolderMask(Top)) = Rectangle(3.400, 3.400) + +public pcb-landpattern RP2350B_QFN-80_EP_10_573x10_573_Pitch0_4mm : + pad p[0] : circle-th-pad at loc(-0.850, 0.850) on Top + pad p[1] : rect-smd-pad-1 at loc(-4.896, 3.800, 90.000) on Top + pad p[2] : rect-smd-pad-1 at loc(-4.896, 3.400, 90.000) on Top + pad p[3] : rect-smd-pad-1 at loc(-4.896, 3.000, 90.000) on Top + pad p[4] : rect-smd-pad-1 at loc(-4.896, 2.600, 90.000) on Top + pad p[5] : rect-smd-pad-1 at loc(-4.896, 2.200, 90.000) on Top + pad p[6] : rect-smd-pad-1 at loc(-4.896, 1.800, 90.000) on Top + pad p[7] : rect-smd-pad-1 at loc(-4.896, 1.400, 90.000) on Top + pad p[8] : rect-smd-pad-1 at loc(-4.896, 1.000, 90.000) on Top + pad p[9] : rect-smd-pad-1 at loc(-4.896, 0.600, 90.000) on Top + pad p[10] : rect-smd-pad-1 at loc(-4.896, 0.200, 90.000) on Top + pad p[11] : rect-smd-pad-1 at loc(-4.896, -0.200, 90.000) on Top + pad p[12] : rect-smd-pad-1 at loc(-4.896, -0.600, 90.000) on Top + pad p[13] : rect-smd-pad-1 at loc(-4.896, -1.000, 90.000) on Top + pad p[14] : rect-smd-pad-1 at loc(-4.896, -1.400, 90.000) on Top + pad p[15] : rect-smd-pad-1 at loc(-4.896, -1.800, 90.000) on Top + pad p[16] : rect-smd-pad-1 at loc(-4.896, -2.200, 90.000) on Top + pad p[17] : rect-smd-pad-1 at loc(-4.896, -2.600, 90.000) on Top + pad p[18] : rect-smd-pad-1 at loc(-4.896, -3.000, 90.000) on Top + pad p[19] : rect-smd-pad-1 at loc(-4.896, -3.400, 90.000) on Top + pad p[20] : rect-smd-pad-1 at loc(-4.896, -3.800, 90.000) on Top + pad p[21] : rect-smd-pad-1 at loc(-3.800, -4.896) on Top + pad p[22] : rect-smd-pad-1 at loc(-3.400, -4.896) on Top + pad p[23] : rect-smd-pad-1 at loc(-3.000, -4.896) on Top + pad p[24] : rect-smd-pad-1 at loc(-2.600, -4.896) on Top + pad p[25] : rect-smd-pad-1 at loc(-2.200, -4.896) on Top + pad p[26] : rect-smd-pad-1 at loc(-1.800, -4.896) on Top + pad p[27] : rect-smd-pad-1 at loc(-1.400, -4.896) on Top + pad p[28] : rect-smd-pad-1 at loc(-1.000, -4.896) on Top + pad p[29] : rect-smd-pad-1 at loc(-0.600, -4.896) on Top + pad p[30] : rect-smd-pad-1 at loc(-0.200, -4.896) on Top + pad p[31] : rect-smd-pad-1 at loc(0.200, -4.896) on Top + pad p[32] : rect-smd-pad-1 at loc(0.600, -4.896) on Top + pad p[33] : rect-smd-pad-1 at loc(1.000, -4.896) on Top + pad p[34] : rect-smd-pad-1 at loc(1.400, -4.896) on Top + pad p[35] : rect-smd-pad-1 at loc(1.800, -4.896) on Top + pad p[36] : rect-smd-pad-1 at loc(2.200, -4.896) on Top + pad p[37] : rect-smd-pad-1 at loc(2.600, -4.896) on Top + pad p[38] : rect-smd-pad-1 at loc(3.000, -4.896) on Top + pad p[39] : rect-smd-pad-1 at loc(3.400, -4.896) on Top + pad p[40] : rect-smd-pad-1 at loc(3.800, -4.896) on Top + pad p[41] : rect-smd-pad-1 at loc(4.896, -3.800, 90.000) on Top + pad p[42] : rect-smd-pad-1 at loc(4.896, -3.400, 90.000) on Top + pad p[43] : rect-smd-pad-1 at loc(4.896, -3.000, 90.000) on Top + pad p[44] : rect-smd-pad-1 at loc(4.896, -2.600, 90.000) on Top + pad p[45] : rect-smd-pad-1 at loc(4.896, -2.200, 90.000) on Top + pad p[46] : rect-smd-pad-1 at loc(4.896, -1.800, 90.000) on Top + pad p[47] : rect-smd-pad-1 at loc(4.896, -1.400, 90.000) on Top + pad p[48] : rect-smd-pad-1 at loc(4.896, -1.000, 90.000) on Top + pad p[49] : rect-smd-pad-1 at loc(4.896, -0.600, 90.000) on Top + pad p[50] : rect-smd-pad-1 at loc(4.896, -0.200, 90.000) on Top + pad p[51] : rect-smd-pad-1 at loc(4.896, 0.200, 90.000) on Top + pad p[52] : rect-smd-pad-1 at loc(4.896, 0.600, 90.000) on Top + pad p[53] : rect-smd-pad-1 at loc(4.896, 1.000, 90.000) on Top + pad p[54] : rect-smd-pad-1 at loc(4.896, 1.400, 90.000) on Top + pad p[55] : rect-smd-pad-1 at loc(4.896, 1.800, 90.000) on Top + pad p[56] : rect-smd-pad-1 at loc(4.896, 2.200, 90.000) on Top + pad p[57] : rect-smd-pad-1 at loc(4.896, 2.600, 90.000) on Top + pad p[58] : rect-smd-pad-1 at loc(4.896, 3.000, 90.000) on Top + pad p[59] : rect-smd-pad-1 at loc(4.896, 3.400, 90.000) on Top + pad p[60] : rect-smd-pad-1 at loc(4.896, 3.800, 90.000) on Top + pad p[61] : rect-smd-pad-1 at loc(3.800, 4.896) on Top + pad p[62] : rect-smd-pad-1 at loc(3.400, 4.896) on Top + pad p[63] : rect-smd-pad-1 at loc(3.000, 4.896) on Top + pad p[64] : rect-smd-pad-1 at loc(2.600, 4.896) on Top + pad p[65] : rect-smd-pad-1 at loc(2.200, 4.896) on Top + pad p[66] : rect-smd-pad-1 at loc(1.800, 4.896) on Top + pad p[67] : rect-smd-pad-1 at loc(1.400, 4.896) on Top + pad p[68] : rect-smd-pad-1 at loc(1.000, 4.896) on Top + pad p[69] : rect-smd-pad-1 at loc(0.600, 4.896) on Top + pad p[70] : rect-smd-pad-1 at loc(0.200, 4.896) on Top + pad p[71] : rect-smd-pad-1 at loc(-0.200, 4.896) on Top + pad p[72] : rect-smd-pad-1 at loc(-0.600, 4.896) on Top + pad p[73] : rect-smd-pad-1 at loc(-1.000, 4.896) on Top + pad p[74] : rect-smd-pad-1 at loc(-1.400, 4.896) on Top + pad p[75] : rect-smd-pad-1 at loc(-1.800, 4.896) on Top + pad p[76] : rect-smd-pad-1 at loc(-2.200, 4.896) on Top + pad p[77] : rect-smd-pad-1 at loc(-2.600, 4.896) on Top + pad p[78] : rect-smd-pad-1 at loc(-3.000, 4.896) on Top + pad p[79] : rect-smd-pad-1 at loc(-3.400, 4.896) on Top + pad p[80] : rect-smd-pad-1 at loc(-3.800, 4.896) on Top + pad p[81] : circle-th-pad at loc(-0.850, 0.0) on Top + pad p[82] : circle-th-pad at loc(-0.850, -0.850) on Top + pad p[83] : circle-th-pad at loc(0.0, 0.850) on Top + pad p[84] : circle-th-pad at loc(0.0, 0.0) on Top + pad p[85] : rect-smd-pad-2 at loc(0.0, 0.0) on Top + pad p[86] : circle-th-pad at loc(0.0, -0.850) on Top + pad p[87] : circle-th-pad at loc(0.850, 0.850) on Top + pad p[88] : circle-th-pad at loc(0.850, 0.0) on Top + pad p[89] : circle-th-pad at loc(0.850, -0.850) on Top + + layer(Courtyard(Top)) = Polygon([ + Point(-5.950, 5.950) + Point(5.950, 5.950) + Point(5.950, -5.950) + Point(-5.950, -5.950)]) + layer(Silkscreen("F-SilkS", Top)) = Line(0.120, [Point(-5.436, -5.436), Point(-5.436, -4.200)]) + layer(Silkscreen("F-SilkS", Top)) = Line(0.120, [Point(-4.200, -5.436), Point(-5.436, -5.436)]) + layer(Silkscreen("F-SilkS", Top)) = Line(0.120, [Point(4.200, 5.436), Point(5.436, 5.436)]) + layer(Silkscreen("F-SilkS", Top)) = Line(0.120, [Point(4.200, -5.436), Point(5.436, -5.436)]) + layer(Silkscreen("F-SilkS", Top)) = Line(0.120, [Point(5.436, 5.436), Point(5.436, 4.200)]) + layer(Silkscreen("F-SilkS", Top)) = Line(0.120, [Point(5.436, -5.436), Point(5.436, -4.200)]) + layer(Silkscreen("F-SilkS", Top)) = Circle(-5.356, 5.334, 0.479) + layer(CustomLayer("Fab", Top)) = Line(0.150, [Point(-5.286, 4.286), Point(-5.286, -5.286)]) + layer(CustomLayer("Fab", Top)) = Line(0.150, [Point(-5.286, -5.286), Point(5.286, -5.286)]) + layer(CustomLayer("Fab", Top)) = Line(0.150, [Point(-4.286, 5.286), Point(-5.286, 4.286)]) + layer(CustomLayer("Fab", Top)) = Line(0.150, [Point(5.286, 5.286), Point(-4.286, 5.286)]) + layer(CustomLayer("Fab", Top)) = Line(0.150, [Point(5.286, -5.286), Point(5.286, 5.286)]) + layer(CustomLayer("Fab", Top)) = Text("${REFERENCE}", 1.0, C, loc(0.0, 0.0)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-1.275, 1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-1.275, 0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-1.275, -0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-1.275, -1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-0.425, 1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-0.425, 0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-0.425, -0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(-0.425, -1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(0.425, 1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(0.425, 0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(0.425, -0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(0.425, -1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(1.275, 1.275)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(1.275, 0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(1.275, -0.425)) + layer(Paste(Top)) = Rectangle(0.100, 0.100, loc(1.275, -1.275)) + diff --git a/Firmware/LogicAnalyzer_V2/.vscode/c_cpp_properties.json b/Firmware/LogicAnalyzer_V2/.vscode/c_cpp_properties.json index dae013cf..b0ab8ef3 100644 --- a/Firmware/LogicAnalyzer_V2/.vscode/c_cpp_properties.json +++ b/Firmware/LogicAnalyzer_V2/.vscode/c_cpp_properties.json @@ -1,22 +1,22 @@ -{ - "configurations": [ - { - "name": "Pico", - "includePath": [ - "${workspaceFolder}/**", - "${userHome}/.pico-sdk/sdk/2.0.0/**" - ], - "forcedInclude": [ - "${userHome}/.pico-sdk/sdk/2.0.0/src/common/pico_base_headers/include/pico.h", - "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h" - ], - "defines": [], - "compilerPath": "${userHome}/.pico-sdk/toolchain/13_2_Rel1/bin/arm-none-eabi-gcc.exe", - "compileCommands": "${workspaceFolder}/build/compile_commands.json", - "cStandard": "c17", - "cppStandard": "c++14", - "intelliSenseMode": "linux-gcc-arm" - } - ], - "version": 4 -} +{ + "configurations": [ + { + "name": "Pico", + "includePath": [ + "${workspaceFolder}/**", + "${userHome}/.pico-sdk/sdk/2.1.1/**" + ], + "forcedInclude": [ + "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h", + "${userHome}/.pico-sdk/sdk/2.1.1/src/common/pico_base_headers/include/pico.h" + ], + "defines": [], + "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "cStandard": "c17", + "cppStandard": "c++14", + "intelliSenseMode": "linux-gcc-arm" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/.vscode/launch.json b/Firmware/LogicAnalyzer_V2/.vscode/launch.json index 1df8f9ea..5eabd312 100644 --- a/Firmware/LogicAnalyzer_V2/.vscode/launch.json +++ b/Firmware/LogicAnalyzer_V2/.vscode/launch.json @@ -15,7 +15,7 @@ "interface/cmsis-dap.cfg", "target/${command:raspberry-pi-pico.getTarget}.cfg" ], - "svdFile": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChip}.svd", + "svdFile": "${userHome}/.pico-sdk/sdk/2.1.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChip}.svd", "runToEntryPoint": "main", // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected // Also works fine for flash binaries @@ -37,7 +37,7 @@ "gdbTarget": "localhost:3333", "gdbPath": "${command:raspberry-pi-pico.getGDBPath}", "device": "${command:raspberry-pi-pico.getChip}", - "svdFile": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChip}.svd", + "svdFile": "${userHome}/.pico-sdk/sdk/2.1.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChip}.svd", "runToEntryPoint": "main", // Give restart the same functionality as runToEntryPoint - main "postRestartCommands": [ @@ -63,7 +63,7 @@ "limit": 4 }, "preLaunchTask": "Flash", - "svdPath": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChip}.svd" + "svdPath": "${userHome}/.pico-sdk/sdk/2.1.1/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChip}.svd" }, ] } diff --git a/Firmware/LogicAnalyzer_V2/.vscode/settings.json b/Firmware/LogicAnalyzer_V2/.vscode/settings.json index a52ddfac..046e5a62 100644 --- a/Firmware/LogicAnalyzer_V2/.vscode/settings.json +++ b/Firmware/LogicAnalyzer_V2/.vscode/settings.json @@ -15,26 +15,26 @@ "cmake.automaticReconfigure": false, "cmake.configureOnOpen": false, "cmake.generator": "Ninja", - "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.28.6/bin/cmake", + "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake", "C_Cpp.debugShortcut": false, "terminal.integrated.env.windows": { - "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.0.0", - "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/13_2_Rel1", - "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/13_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.0.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.28.6/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" + "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.1.1", + "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1", + "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.0.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}" }, "terminal.integrated.env.osx": { - "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.0.0", - "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/13_2_Rel1", - "PATH": "${env:HOME}/.pico-sdk/toolchain/13_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.0.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.28.6/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.1.1", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.0.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" }, "terminal.integrated.env.linux": { - "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.0.0", - "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/13_2_Rel1", - "PATH": "${env:HOME}/.pico-sdk/toolchain/13_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.0.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.28.6/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" + "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.1.1", + "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1", + "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.0.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}" }, "raspberry-pi-pico.cmakeAutoConfigure": true, "raspberry-pi-pico.useCmakeTools": false, - "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.28.6/bin/cmake", + "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake", "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja", "raspberry-pi-pico.python3Path": "${HOME}/.pico-sdk/python/3.12.1/python.exe", "files.associations": { @@ -45,6 +45,8 @@ "string.h": "c", "stdio.h": "c", "dma.h": "c", - "cstdlib": "c" + "cstdlib": "c", + "cstdint": "c", + "string_view": "c" } } diff --git a/Firmware/LogicAnalyzer_V2/CMakeLists.txt b/Firmware/LogicAnalyzer_V2/CMakeLists.txt index 5a4010b4..f3b66e8f 100644 --- a/Firmware/LogicAnalyzer_V2/CMakeLists.txt +++ b/Firmware/LogicAnalyzer_V2/CMakeLists.txt @@ -9,20 +9,23 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Initialise pico_sdk from installed location # (note this can come from environment, CMake cache etc) -# == DO NEVER EDIT THE NEXT LINES for Raspberry Pi Pico VS Code Extension to work == +# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work == if(WIN32) set(USERHOME $ENV{USERPROFILE}) else() set(USERHOME $ENV{HOME}) endif() -set(sdkVersion 2.0.0) -set(toolchainVersion 13_2_Rel1) -set(picotoolVersion 2.0.0) -include(LogicAnalyzer_Build_Settings.cmake) -include(${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +set(sdkVersion 2.2.0) +set(toolchainVersion 14_2_Rel1) +set(picotoolVersion 2.1.1) +set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) +if (EXISTS ${picoVscode}) + include(${picoVscode}) +endif() +# ==================================================================================== +include(LogicAnalyzer_Build_Settings.cmake) -# ==================================================================================== if(NOT BOARD_TYPE) message(FATAL_ERROR "Board not set, configure the build on LogicAnalyzer_Build_Settings.cmake") endif() @@ -35,9 +38,20 @@ if((BOARD_TYPE STREQUAL "BOARD_PICO_W") OR (BOARD_TYPE STREQUAL "BOARD_PICO_W_WI if(TURBO_MODE) message(FATAL_ERROR "Cannot enable turbo mode for the Pico W") endif() +elseif((BOARD_TYPE STREQUAL "BOARD_PICO_2_W") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2_W_WIFI")) + message(STATUS "Setting PICO_BOARD to pico2_w") + set(PICO_BOARD pico2_w CACHE STRING "Board type") + message(STATUS "Forcing Debug for W build") + set(CMAKE_BUILD_TYPE Debug) + if(TURBO_MODE) + message(FATAL_ERROR "Cannot enable turbo mode for the Pico 2 W") + endif() elseif(BOARD_TYPE STREQUAL "BOARD_PICO_2") message(STATUS "Setting PICO_BOARD to pico2") set(PICO_BOARD pico2 CACHE STRING "Board type") +elseif(BOARD_TYPE STREQUAL "BOARD_PICO2_ICE") + message(STATUS "Setting PICO_BOARD to pico2") + set(PICO_BOARD pico2 CACHE STRING "Board type") else() message(STATUS "Setting PICO_BOARD to pico") set(PICO_BOARD pico CACHE STRING "Board type") @@ -49,7 +63,7 @@ if(TURBO_MODE) endif() if(NOT DEBUG_BUILD) - if((BOARD_TYPE STREQUAL "BOARD_PICO") OR (BOARD_TYPE STREQUAL "BOARD_ZERO") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2")) + if((BOARD_TYPE STREQUAL "BOARD_PICO") OR (BOARD_TYPE STREQUAL "BOARD_ZERO") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2") OR (BOARD_TYPE STREQUAL "BOARD_PICO2_ICE")) message(STATUS "Forcing Release for RAM-only build") set(CMAKE_BUILD_TYPE Release) endif() @@ -71,7 +85,7 @@ ADD_EXECUTABLE(LogicAnalyzer ${CSources}) # Enable ram-only build for the base pico and zero, increases timming precission if(NOT DEBUG_BUILD) - if((BOARD_TYPE STREQUAL "BOARD_PICO") OR (BOARD_TYPE STREQUAL "BOARD_ZERO") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2")) + if((BOARD_TYPE STREQUAL "BOARD_PICO") OR (BOARD_TYPE STREQUAL "BOARD_ZERO") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2") OR (BOARD_TYPE STREQUAL "BOARD_PICO2_ICE")) message(STATUS "Setting RAM-only compilation") pico_set_binary_type(LogicAnalyzer copy_to_ram) endif() @@ -80,11 +94,12 @@ endif() # Create C header file with the name .pio.h pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/LogicAnalyzer.pio + ${CMAKE_CURRENT_LIST_DIR}/fpga_clock.pio ) pico_set_program_name(LogicAnalyzer "LogicAnalyzer") -pico_set_program_version(LogicAnalyzer "6.0") -add_compile_definitions(FIRMWARE_VERSION="V6_0") +pico_set_program_version(LogicAnalyzer "6.5") +add_compile_definitions(FIRMWARE_VERSION="V6_5") pico_enable_stdio_uart(LogicAnalyzer 0) pico_enable_stdio_usb(LogicAnalyzer 1) @@ -96,6 +111,7 @@ endif() if(BOARD_TYPE STREQUAL "BOARD_PICO_2") message(STATUS "Configuring for Pico 2") add_compile_definitions(BUILD_PICO_2) + add_compile_definitions(CORE_TYPE_2) endif() if(BOARD_TYPE STREQUAL "BOARD_PICO_W") message(STATUS "Configuring for Pico W without WiFi support") @@ -108,12 +124,38 @@ if(BOARD_TYPE STREQUAL "BOARD_PICO_W_WIFI") add_compile_definitions(BUILD_PICO_W_WIFI) set (CYW_LIB pico_cyw43_arch_lwip_poll) endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_2_W") + message(STATUS "Configuring for Pico W without WiFi support") + set(PICO_BOARD pico2_w) + add_compile_definitions(BUILD_PICO_2_W) + add_compile_definitions(CORE_TYPE_2) + set (CYW_LIB pico_cyw43_arch_none) +endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_2_W_WIFI") + message(STATUS "Configuring for Pico W with WiFi support") + add_compile_definitions(BUILD_PICO_2_W_WIFI) + add_compile_definitions(CORE_TYPE_2) + set (CYW_LIB pico_cyw43_arch_lwip_poll) +endif() if(BOARD_TYPE STREQUAL "BOARD_ZERO") message(STATUS "Configuring for Zero") add_compile_definitions(BUILD_ZERO) endif() +if(BOARD_TYPE STREQUAL "BOARD_INTERCEPTOR") + message(STATUS "Configuring for Interceptor") + add_compile_definitions(BUILD_INTERCEPTOR) +endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_ICE") + message(STATUS "Configuring for Pico-ICE") + add_compile_definitions(BUILD_PICO_ICE) +endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO2_ICE") + message(STATUS "Configuring for Pico2-ICE") + add_compile_definitions(BUILD_PICO2_ICE) + add_compile_definitions(CORE_TYPE_2) +endif() -# Add any user requested libraries +# Add any user requested librariesUna pregu target_link_libraries(LogicAnalyzer pico_stdlib hardware_dma diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c index 5c24c8cf..21c15681 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer.c @@ -1,5 +1,9 @@ #include "LogicAnalyzer_Board_Settings.h" +#if defined(BUILD_PICO_ICE) || defined(BUILD_PICO2_ICE) + #include "LogicAnalyzer_FPGA.h" +#endif + #include #include #include "pico/stdlib.h" @@ -79,6 +83,10 @@ #define INIT_LED() init_rgb() #define LED_ON() send_rgb(0,32,0) #define LED_OFF() send_rgb(0,0,32) +#elif defined (NO_LED) + #define INIT_LED() { } + #define LED_ON() { } + #define LED_OFF() { } #endif //Buffer used to store received data @@ -198,6 +206,33 @@ void cdc_transfer(unsigned char* data, int len) } } +#ifdef USE_CYGW_WIFI +/// @brief Transfer a buffer of data through WiFi +/// @param data Buffer of data to transfer +/// @param len Length of the buffer +void wifi_transfer(unsigned char* data, int len) +{ + EVENT_FROM_FRONTEND evt; + evt.event = SEND_DATA; + + int pos = 0; + int filledData; + while(pos < len) + { + filledData = 0; + while(pos < len && filledData < 32) + { + evt.data[filledData] = data[pos]; + pos++; + filledData++; + } + + evt.dataLength = filledData; + event_push(&frontendToWifi, &evt); + } +} +#endif + /// @brief Processes data received from the host application /// @param data The received data /// @param length Length of the data @@ -311,6 +346,7 @@ void processData(uint8_t* data, uint length, bool fromWiFi) wReq = (WIFI_SETTINGS_REQUEST*)&messageBuffer[3]; WIFI_SETTINGS settings; + settings.checksum = 0; memcpy(settings.apName, wReq->apName, 33); memcpy(settings.passwd, wReq->passwd, 64); memcpy(settings.ipAddress, wReq->ipAddress, 16); @@ -553,7 +589,7 @@ int main() vreg_disable_voltage_limit(); vreg_set_voltage(VREG_VOLTAGE_1_30); - sleep_ms(1); + sleep_ms(100); //Overclock Powerrrr! set_sys_clock_khz(400000, true); @@ -582,9 +618,14 @@ int main() //Initialize USB stdio stdio_init_all(); - #if defined (BUILD_PICO_W) + #if defined(BUILD_PICO_ICE) || defined(BUILD_PICO2_ICE) + // Initialize FPGA (release reset, wait for CDONE) and start 10 MHz clock on GPIO24 + fpga_init(); + #endif + + #if defined (BUILD_PICO_W) || defined (BUILD_PICO_2_W) cyw43_arch_init(); - #elif defined (BUILD_PICO_W_WIFI) + #elif defined (BUILD_PICO_W_WIFI) || defined (BUILD_PICO_2_W_WIFI) event_machine_init(&wifiToFrontend, wifiEvent, sizeof(EVENT_FROM_WIFI), 8); multicore_launch_core1(runWiFiCore); while(!cywReady) @@ -621,22 +662,23 @@ int main() uint8_t* lengthPointer = (uint8_t*)&length; //Send capture length - sleep_ms(100); + #ifdef USE_CYGW_WIFI if(usbDisabled) { - EVENT_FROM_FRONTEND evt; - evt.event = SEND_DATA; - evt.dataLength = 4; - memcpy(evt.data, lengthPointer, 4); - event_push(&frontendToWifi, &evt); + sleep_ms(2000); + wifi_transfer(lengthPointer, 4); } else + { + sleep_ms(100); cdc_transfer(lengthPointer, 4); + } #else + sleep_ms(100); cdc_transfer(lengthPointer, 4); #endif @@ -660,39 +702,18 @@ int main() //Send the samples if(usbDisabled) { - EVENT_FROM_FRONTEND evt; - evt.event = SEND_DATA; - - int pos = 0; - int filledData; - while(pos < length) + if(first + length > CAPTURE_BUFFER_SIZE) { - filledData = 0; - while(pos < length && filledData < 32) - { - evt.data[filledData] = buffer[first++]; - - if(first >= 131072) - first = 0; - - pos++; - filledData++; - } - - evt.dataLength = filledData; - event_push(&frontendToWifi, &evt); + wifi_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first); + wifi_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE); } + else + wifi_transfer(buffer + first, length); - evt.data[0] = stampsLength; - evt.dataLength = 1; - event_push(&frontendToWifi, &evt); + wifi_transfer(&stampsLength, 1); - for(int buc = 0; buc < stampsLength; buc++) - { - *((uint32_t*)evt.data) = timestamps[buc]; - evt.dataLength = 4; - event_push(&frontendToWifi, &evt); - } + if(stampsLength > 1) + wifi_transfer((unsigned char*)timestamps, stampsLength * 4); } else { diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer.pio b/Firmware/LogicAnalyzer_V2/LogicAnalyzer.pio index c21cd54b..27856a64 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer.pio +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer.pio @@ -175,14 +175,14 @@ INNER_LOOP: .wrap_target - in pins 29 ;read sample + in pins 32 ;read sample jmp pin POST_CAPTURE ;exit wrap if pin is set .wrap POST_CAPTURE: - in pins 29 ;read sample + in pins 32 ;read sample jmp x-- POST_CAPTURE ;loop if more samples needed irq 0 ;notify to the main program that we have finished capturing @@ -199,14 +199,14 @@ LOCK: .wrap_target - in pins 29 ;read sample + in pins 32 ;read sample jmp pin POST_CAPTURE ;exit wrap if pin is set .wrap POST_CAPTURE: - in pins 29 ;read sample + in pins 32 ;read sample jmp x-- POST_CAPTURE ;loop if more samples needed irq 0 ;notify to the main program that we have finished capturing diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h index 0d02d032..a6e3adad 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Board_Settings.h @@ -9,33 +9,54 @@ //This defines the name sent to the software //#define BOARD_NAME "PICO" + //If defined the device supports complex, fast and external triggers //#define SUPPORTS_COMPLEX_TRIGGER + //Stablishes the channel base GPIO //#define INPUT_PIN_BASE 2 + //Complex/fast/ext trigger output pin //#define COMPLEX_TRIGGER_OUT_PIN 0 + //Complex/fast/ext trigger input pin //#define COMPLEX_TRIGGER_IN_PIN 1 + //If defined, the onboard led is a led connected to a GPIO //#define GPIO_LED + //If defined, the onboard led is a led connected to a CYGW module (for the Pico W) //#define CYGW_LED + //If defined, the onboard led is a RGB led connected to a GPIO //#define WS2812_LED + + //If defined, the board has no LED + //#define NO_LED + //Defines the used GPIO used for the GPIO and WS2812 led types //#define LED_IO 25 + //If defined enables the Pico W WiFi module //#define USE_CYGW_WIFI + + //Defines the pin map for the PIO + //Include ALWAYS an entry for the COMPLEX_TRIGGER_IN_PIN even if not used, you can repeat a pin if needed + //If COMPLEX_TRIGGER is supported remember to set the first 16 channels to be consecutive + //#define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} + //Defines the maximum capture buffer size //#define CAPTURE_BUFFER_SIZE (128 * 1024) + //Defines the maximum number of channels //#define MAX_CHANNELS 24 + //Defines the maximum frequency for the capture in normal mode //#define MAX_FREQ 200000000 + //Defines the maximum frequency for the capture in blast mode //#define MAX_BLAST_FREQ 400000000 - // + //If the board supports TURBO mode (400Mhz overclock) then you can define two sets of frequencies using //#ifdef TURBO_MODE / #else / #endif @@ -48,6 +69,7 @@ #define COMPLEX_TRIGGER_IN_PIN 1 #define GPIO_LED #define LED_IO 25 + #define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} #ifdef TURBO_MODE #define MAX_FREQ 200000000 @@ -58,7 +80,7 @@ #endif #define CAPTURE_BUFFER_SIZE (128 * 1024) #define MAX_CHANNELS 24 - + #elif defined (BUILD_PICO_2) #define BOARD_NAME "PICO_2" @@ -68,6 +90,7 @@ #define COMPLEX_TRIGGER_IN_PIN 1 #define GPIO_LED #define LED_IO 25 + #define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} #ifdef TURBO_MODE #define MAX_FREQ 200000000 @@ -87,6 +110,7 @@ #define COMPLEX_TRIGGER_OUT_PIN 0 #define COMPLEX_TRIGGER_IN_PIN 1 #define CYGW_LED + #define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} #define MAX_FREQ 100000000 #define MAX_BLAST_FREQ 200000000 @@ -102,11 +126,41 @@ #define COMPLEX_TRIGGER_IN_PIN 1 #define CYGW_LED #define USE_CYGW_WIFI - + #define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} #define MAX_FREQ 100000000 #define MAX_BLAST_FREQ 200000000 #define CAPTURE_BUFFER_SIZE (128 * 1024) #define MAX_CHANNELS 24 + + #elif defined (BUILD_PICO_2_W) + + #define BOARD_NAME "2_W" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define CYGW_LED + #define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} + + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #define CAPTURE_BUFFER_SIZE (128 * 3 * 1024) + #define MAX_CHANNELS 24 + + #elif defined (BUILD_PICO_2_W_WIFI) + + #define BOARD_NAME "2_WIFI" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define CYGW_LED + #define USE_CYGW_WIFI + #define PIN_MAP {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN} + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #define CAPTURE_BUFFER_SIZE (128 * 3 * 1024) + #define MAX_CHANNELS 24 #elif defined (BUILD_ZERO) @@ -117,6 +171,7 @@ #define COMPLEX_TRIGGER_IN_PIN 18 #define WS2812_LED #define LED_IO 16 + #define PIN_MAP {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,26,27,28,29,22,23,24,25,COMPLEX_TRIGGER_IN_PIN} #ifdef TURBO_MODE #define MAX_FREQ 200000000 @@ -127,6 +182,92 @@ #endif #define CAPTURE_BUFFER_SIZE (128 * 1024) #define MAX_CHANNELS 24 + + #elif defined (BUILD_INTERCEPTOR) + + #define BOARD_NAME "INTERCEPTOR" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define NO_LED + #define PIN_MAP {6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,2,3,4,5,COMPLEX_TRIGGER_IN_PIN} + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 28 + + #elif defined (BUILD_PICO_ICE) + + #define BOARD_NAME "PICO_ICE" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 0 + #define COMPLEX_TRIGGER_OUT_PIN 0 // You can connect ICE_27 to ICE_25 if you want complex trigger. + #define COMPLEX_TRIGGER_IN_PIN 1 + #define GPIO_LED + #define LED_IO 12 // Use green LED to avoid conflict with capture pins + #define PIN_MAP {0,1,2,3,4,5,6,7,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,COMPLEX_TRIGGER_IN_PIN} + + // FPGA control pins for pico-ice + #define PIN_FPGA_CRESETN 27 // CRESET_B (active-low) + #define PIN_FPGA_CDONE 26 // CDONE + #define PIN_CLOCK 24 // Clock to FPGA (10MHz) + + // FPGA SPI configuration pins (set as high-Z during config) + #define PIN_ICE_SI 8 // SPI MOSI to FPGA flash + #define PIN_ICE_SO 11 // SPI MISO from FPGA flash + #define PIN_ICE_SCK 10 // SPI clock to FPGA flash + #define PIN_ICE_SSN 9 // SPI CS to FPGA flash (active-low) + #define PIN_RAM_SS 14 // PSRAM chip select + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 24 + + #elif defined (BUILD_PICO2_ICE) + + #define BOARD_NAME "PICO2_ICE" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 20 + #define COMPLEX_TRIGGER_OUT_PIN 2 // GPIO0 and GPIO1 are not connected to headers. + #define COMPLEX_TRIGGER_IN_PIN 3 // so we are trying GPIO2 and GPIO3 here. + #define GPIO_LED + #define LED_IO 0 // Use green LED (RGB LED - green component) + #define PIN_MAP {20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,COMPLEX_TRIGGER_IN_PIN} + + // FPGA control pins for pico2-ice + #define PIN_FPGA_CRESETN 31 // CRESET_B (active-low) + #define PIN_FPGA_CDONE 40 // CDONE + #define PIN_CLOCK 21 // Clock to FPGA + + // FPGA SPI configuration pins + #define PIN_ICE_SI 4 // SPI MOSI to FPGA flash + #define PIN_ICE_SO 7 // SPI MISO from FPGA flash + #define PIN_ICE_SCK 6 // SPI clock to FPGA flash + #define PIN_ICE_SSN 5 // SPI CS to FPGA flash (active-low) + #define PIN_RAM_SS -1 // No external PSRAM + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 3 * 1024) // Use larger buffer like other RP2350 boards + #define MAX_CHANNELS 24 #endif diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake index a5f2665b..642f82d6 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Build_Settings.cmake @@ -1,10 +1,11 @@ # This file controls the build settings, set your board version -# Current versions: "BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2" -set(BOARD_TYPE "BOARD_PICO_2") +# Current versions: "BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2", "BOARD_PICO_ICE", "BOARD_PICO2_ICE" +set(BOARD_TYPE "BOARD_PICO2_ICE") # Set to 1 to enable 200Mhz mode (warning! extreme overclock and overvoltage!) +# Set to 0 to disable turbo mode # Not available for the Pico W -set(TURBO_MODE 1) +set(TURBO_MODE 0) # Uncomment to be able to debug the build # set(DEBUG_BUILD 1) diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c index a97f7d9e..fc5eac0a 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.c @@ -12,6 +12,10 @@ #include "hardware/structs/bus_ctrl.h" #include "LogicAnalyzer.pio.h" +#if defined(CORE_TYPE_2) +#include +#endif + //Static variables for the PIO programs static PIO capturePIO; static PIO triggerPIO; @@ -28,13 +32,13 @@ static uint32_t dmaPingPong1; static uint32_t transferCount; //Static information of the last capture -static uint8_t lastCapturePins[24]; //List of captured pins -static uint8_t lastCapturePinCount; //Count of captured pins -static uint32_t lastTriggerCapture; //Moment where the trigger happened inside the circular pre buffer -static uint32_t lastPreSize; //Pre-trigger buffer size -static uint32_t lastPostSize; //Post-trigger buffer size -static uint32_t lastLoopCount; //Number of loops -static bool lastTriggerInverted; //Inverted? +static uint8_t lastCapturePins[MAX_CHANNELS]; //List of captured pins +static uint8_t lastCapturePinCount; //Count of captured pins +static uint32_t lastTriggerCapture; //Moment where the trigger happened inside the circular pre buffer +static uint32_t lastPreSize; //Pre-trigger buffer size +static uint32_t lastPostSize; //Post-trigger buffer size +static uint32_t lastLoopCount; //Number of loops +static bool lastTriggerInverted; //Inverted? static uint8_t lastTriggerPin; static uint32_t lastStartPosition; static bool lastCaptureComplexFast; @@ -56,18 +60,8 @@ static exception_handler_t oldNMIHandler; static exception_handler_t oldSysTickHandler; //Pin mapping, used to map the channels to the PIO program -//COMPLEX_TRIGGER_IN_PIN is added at the end of the array to support the chained mode -#if defined (BUILD_PICO) - const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; -#elif defined (BUILD_PICO_2) - const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; -#elif defined (BUILD_PICO_W) - const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; -#elif defined (BUILD_PICO_W_WIFI) - const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; -#elif defined (BUILD_ZERO) - const uint8_t pinMap[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,26,27,28,29,22,23,24,25,COMPLEX_TRIGGER_IN_PIN}; -#endif + +const uint8_t pinMap[] = PIN_MAP; //Main capture buffer, aligned at a dword boundary. static uint8_t captureBuffer[CAPTURE_BUFFER_SIZE] __attribute__((aligned(4))); @@ -219,11 +213,23 @@ void disable_gpios() gpio_deinit(COMPLEX_TRIGGER_IN_PIN); #endif + // Universal pin state preservation: Don't change ANY pin states after capture + // This preserves all pin configurations (inputs, outputs, high-Z) for user applications + // Logic Analyzer only needs to READ pins during capture, not control them after + // Comment out this entire section if you need the old behavior that changed pins to high-Z + /* for(uint8_t i = 0; i < lastCapturePinCount; i++) - gpio_deinit(lastCapturePins[i]); - + { + gpio_deinit(lastCapturePins[i]); // Old behavior - disrupted user applications + } + */ - gpio_set_inover(lastTriggerPin, 0); + // Universal trigger pin preservation: Only reset inover for input trigger pins + // This preserves output trigger pins in their output state for user applications + if (!gpio_is_dir_out(lastTriggerPin)) + { + gpio_set_inover(lastTriggerPin, 0); + } } @@ -401,7 +407,7 @@ void simple_capture_completed() if(timestampIndex) { -#if defined(BUILD_PICO_2) +#if defined(CORE_TYPE_2) EPPB->NMI_MASK0 = 0; #else syscfg_hw->proc0_nmi_mask = 0; @@ -606,7 +612,7 @@ bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co return false; //Incorrect pin count? - if(capturePinCount < 0 || capturePinCount > MAX_CHANNELS) + if(capturePinCount < 1 || capturePinCount > MAX_CHANNELS) return false; //Bad trigger? @@ -641,12 +647,20 @@ bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co pio_clear_instruction_memory(triggerPIO); - //Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins) + //Configure MAX_CHANNELS + 2 IO's to be used by the PIO (MAX_CHANNELS channels + 2 trigger pins) pio_gpio_init(triggerPIO, COMPLEX_TRIGGER_OUT_PIN); pio_gpio_init(capturePIO, COMPLEX_TRIGGER_IN_PIN); - for(uint8_t i = 0; i < 24; i++) - pio_gpio_init(capturePIO, pinMap[i]); + for(uint8_t i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure capture SM sm_Capture = pio_claim_unused_sm(capturePIO, true); @@ -655,8 +669,16 @@ bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co captureOffset = pio_add_program(capturePIO, &FAST_CAPTURE_program); //Modified for the W - for(int i = 0; i < 24; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + for(int i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } //Configure state machines pio_sm_config smConfig = FAST_CAPTURE_program_get_default_config(captureOffset); @@ -667,8 +689,8 @@ bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, co //Set clock to 2x required frequency sm_config_set_clkdiv(&smConfig, clockDiv); - //Autopush per 29 bits - sm_config_set_in_shift(&smConfig, false, true, 29); + //Autopush per dword + sm_config_set_in_shift(&smConfig, false, true, 0); //Configure fast trigger pin (COMPLEX_TRIGGER_IN_PIN) as JMP pin. sm_config_set_jmp_pin(&smConfig, COMPLEX_TRIGGER_IN_PIN); @@ -775,7 +797,7 @@ bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, return false; //Incorrect pin count? - if(capturePinCount < 0 || capturePinCount > MAX_CHANNELS) + if(capturePinCount < 1 || capturePinCount > MAX_CHANNELS) return false; //Bad trigger? @@ -808,12 +830,20 @@ bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, capturePIO = pio0; pio_clear_instruction_memory(capturePIO); - //Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins) + //Configure MAX_CHANNELS + 2 IO's to be used by the PIO (MAX_CHANNELS channels + 2 trigger pins) pio_gpio_init(capturePIO, COMPLEX_TRIGGER_OUT_PIN); pio_gpio_init(capturePIO, COMPLEX_TRIGGER_IN_PIN); - for(uint8_t i = 0; i < 24; i++) - pio_gpio_init(capturePIO, pinMap[i]); + for(uint8_t i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure capture SM sm_Capture = pio_claim_unused_sm(capturePIO, true); @@ -821,8 +851,16 @@ bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, pio_sm_restart(capturePIO, sm_Capture); captureOffset = pio_add_program(capturePIO, &COMPLEX_CAPTURE_program); - for(int i = 0; i < 24; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + for(int i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } //Configure state machines pio_sm_config smConfig = COMPLEX_CAPTURE_program_get_default_config(captureOffset); @@ -833,8 +871,8 @@ bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, //Set clock to 2x required frequency sm_config_set_clkdiv(&smConfig, clockDiv); - //Autopush per 29 bits - sm_config_set_in_shift(&smConfig, false, true, 29); + //Autopush per dword + sm_config_set_in_shift(&smConfig, false, true, 0); //Configure complex trigger pin (pin COMPLEX_TRIGGER_IN_PIN) as JMP pin. sm_config_set_jmp_pin(&smConfig, COMPLEX_TRIGGER_IN_PIN); @@ -942,6 +980,8 @@ bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePin return false; //Incorrect trigger pin? + //WARNING: comparison of triggerPin and MAX_CHANNELS is correct, we exceed the maximum number of channels by 1 + //as the complex trigger channel is added at the end of the pinMap array if(triggerPin < 0 || triggerPin > MAX_CHANNELS) return false; @@ -981,15 +1021,40 @@ bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePin captureOffset = pio_add_program(capturePIO, &BLAST_CAPTURE_program); //Configure capture pins - for(int i = 0; i < 24; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + for(int i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } - for(uint8_t i = 0; i < 24; i++) - pio_gpio_init(capturePIO, pinMap[i]); + for(uint8_t i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure trigger pin - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); - pio_gpio_init(capturePIO, triggerPin); + // Universal output protection: Only change pin direction for pins not already configured as outputs + if (!gpio_is_dir_out(triggerPin)) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); + } + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This preserves output signals while still allowing them to be monitored for triggers + if (!gpio_is_dir_out(triggerPin)) + { + pio_gpio_init(capturePIO, triggerPin); + } if(!invertTrigger) gpio_set_inover(triggerPin, 1); @@ -1031,7 +1096,7 @@ bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePin return true; } -bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint8_t loopCount, uint8_t measureBursts, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode) +bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint16_t loopCount, uint8_t measureBursts, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode) { int maxSamples; @@ -1056,6 +1121,10 @@ bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, if(freq > MAX_FREQ) return false; + //Too many loops to be measured? + if(measureBursts && loopCount > 253) + return false; + //Incorrect pin count? if(capturePinCount < 1 || capturePinCount > MAX_CHANNELS) return false; @@ -1114,15 +1183,40 @@ bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, } //Configure capture pins - for(int i = 0; i < 24; i++) - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + for(int i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Only change pin direction for pins not already configured as outputs + // This preserves output pins (like FPGA clocks, LEDs, etc.) while still allowing monitoring + // Comment out this loop and use pio_sm_set_consecutive_pindirs() directly to revert + if (!gpio_is_dir_out(pinMap[i])) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + } + } - for(uint8_t i = 0; i < 24; i++) - pio_gpio_init(capturePIO, pinMap[i]); + for(uint8_t i = 0; i < MAX_CHANNELS; i++) + { + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This allows monitoring of output signals while preserving their output state + // Comment out the gpio_is_dir_out() check if you need PIO to control output pins + if (!gpio_is_dir_out(pinMap[i])) + { + pio_gpio_init(capturePIO, pinMap[i]); + } + } //Configure trigger pin - pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); - pio_gpio_init(capturePIO, triggerPin); + // Universal output protection: Only change pin direction for pins not already configured as outputs + if (!gpio_is_dir_out(triggerPin)) + { + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); + } + // Universal output protection: Don't take PIO ownership of pins already configured as outputs + // This preserves output signals while still allowing them to be monitored for triggers + if (!gpio_is_dir_out(triggerPin)) + { + pio_gpio_init(capturePIO, triggerPin); + } //Configure state machines pio_sm_config smConfig = measureBursts? @@ -1163,7 +1257,7 @@ bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, //syscfg_hw->proc0_nmi_mask = 1 << PIO0_IRQ_1; -#if defined(BUILD_PICO_2) +#if defined(CORE_TYPE_2) EPPB->NMI_MASK0 = 1 << PIO0_IRQ_1; #else syscfg_hw->proc0_nmi_mask = 1 << PIO0_IRQ_1; diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.h b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.h index fe54a591..1a3ba93f 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.h +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Capture.h @@ -14,7 +14,7 @@ typedef enum } CHANNEL_MODE; -bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint8_t loopCount, uint8_t measureBursts, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode); +bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint16_t loopCount, uint8_t measureBursts, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode); bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode); #ifdef SUPPORTS_COMPLEX_TRIGGER bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue, CHANNEL_MODE captureMode); diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.c new file mode 100644 index 00000000..fc216b4f --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.c @@ -0,0 +1,191 @@ +#include "LogicAnalyzer_FPGA.h" + +#if defined(BUILD_PICO_ICE) || defined(BUILD_PICO2_ICE) + +#include "hardware/gpio.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" +#include "pico/time.h" +#include "fpga_clock.pio.h" + +// PIO instance for FPGA clock generation (avoid conflicts with logic analyzer) +static PIO fpga_pio = pio1; +static int fpga_sm = -1; +static uint fpga_clock_offset = 0; + +// Use generated PIO program (fpga_clock_program) + +bool fpga_init(void) +{ + // Initialize all FPGA control pins + + // Configure ICE_RESET pin (active-low) - start with FPGA in reset + gpio_init(PIN_FPGA_CRESETN); + gpio_set_dir(PIN_FPGA_CRESETN, GPIO_OUT); + gpio_put(PIN_FPGA_CRESETN, 0); // Hold FPGA in reset initially + + // Configure CDONE pin (input, no pulls) - FPGA asserts this when configuration complete + gpio_init(PIN_FPGA_CDONE); + gpio_set_dir(PIN_FPGA_CDONE, GPIO_IN); + gpio_disable_pulls(PIN_FPGA_CDONE); + + // Leave sysCONFIG SPI pins high-Z so FPGA can self-configure from flash + gpio_init(PIN_ICE_SI); gpio_set_dir(PIN_ICE_SI, GPIO_IN); gpio_disable_pulls(PIN_ICE_SI); + gpio_init(PIN_ICE_SO); gpio_set_dir(PIN_ICE_SO, GPIO_IN); gpio_disable_pulls(PIN_ICE_SO); + gpio_init(PIN_ICE_SCK); gpio_set_dir(PIN_ICE_SCK, GPIO_IN); gpio_disable_pulls(PIN_ICE_SCK); + gpio_init(PIN_ICE_SSN); gpio_set_dir(PIN_ICE_SSN, GPIO_IN); gpio_disable_pulls(PIN_ICE_SSN); + + // Handle PSRAM SS pin (pico-ice has PSRAM, pico2-ice doesn't) + if (PIN_RAM_SS != -1) { + gpio_init(PIN_RAM_SS); + gpio_set_dir(PIN_RAM_SS, GPIO_IN); + gpio_disable_pulls(PIN_RAM_SS); + } + + // Configure clock output pin (will be controlled by PIO) + gpio_init(PIN_CLOCK); + gpio_set_dir(PIN_CLOCK, GPIO_OUT); + gpio_put(PIN_CLOCK, 0); // Start low + + // Small delay to ensure pins are stable + sleep_ms(10); + + // Release FPGA from reset to start configuration from flash + gpio_put(PIN_FPGA_CRESETN, 1); // This should make CRESETN go HIGH + + // Give FPGA time to start configuration + sleep_ms(10); + + // Start clock immediately for both boards due to CDONE voltage level issues + // pico2-ice: Known hardware bug with CDONE voltage levels + // pico-ice: Similar issue, use same workaround for consistency and reliability + if (!fpga_start_clock()) { + return false; // Clock generation failed + } + + // Still wait for CDONE for confirmation and timeout mechanism + uint32_t timeout_count = 0; + const uint32_t timeout_limit = 1000; // 1000ms timeout + + while (!gpio_get(PIN_FPGA_CDONE) && timeout_count < timeout_limit) { + sleep_ms(1); + timeout_count++; + } + +#ifdef BUILD_PICO2_ICE + // For pico2-ice, don't fail on CDONE timeout since the pin reading is unreliable + // Just continue and assume configuration succeeded if we got this far +#else // BUILD_PICO_ICE + // For pico-ice, still report timeout as warning but continue (clock already started) + if (timeout_count >= timeout_limit) { + // FPGA configuration may have failed, but clock is running + // Continue anyway since immediate clock start often works + } +#endif + + return true; +} + +bool fpga_start_clock(void) +{ + // Clear any previous program to free space (only our clock on pio1) + // Note: avoid clearing pio0 which capture uses. + // Load program + if (!pio_can_add_program(fpga_pio, &fpga_clock_program)) { + // try reclaiming a SM and proceed anyway + } + + fpga_clock_offset = pio_add_program(fpga_pio, &fpga_clock_program); + + // Get a free state machine + fpga_sm = pio_claim_unused_sm(fpga_pio, true); + + // Configure SM for sideset pin + pio_sm_config c = fpga_clock_program_get_default_config(fpga_clock_offset); + sm_config_set_sideset_pins(&c, PIN_CLOCK); + + // Prepare the pin + pio_gpio_init(fpga_pio, PIN_CLOCK); + pio_sm_set_consecutive_pindirs(fpga_pio, fpga_sm, PIN_CLOCK, 1, true); + + // Each loop is 2 instructions; with sideset toggling each instr, period = 2 cycles + // f_out = f_sys / (clkdiv * 2) => clkdiv = f_sys / (f_out*2) + float system_freq = (float)clock_get_hz(clk_sys); + float target_freq = 10000000.0f; // 10 MHz + float clkdiv = system_freq / (target_freq * 2.0f); + sm_config_set_clkdiv(&c, clkdiv); + + // Initialize and start SM + pio_sm_init(fpga_pio, fpga_sm, fpga_clock_offset, &c); + pio_sm_set_enabled(fpga_pio, fpga_sm, true); + + return true; +} + +void fpga_stop_clock(void) +{ + if (fpga_sm >= 0) { + pio_sm_set_enabled(fpga_pio, fpga_sm, false); + pio_sm_unclaim(fpga_pio, fpga_sm); + fpga_sm = -1; + } + if (fpga_clock_offset) { + pio_remove_program(fpga_pio, &fpga_clock_program, fpga_clock_offset); + fpga_clock_offset = 0; + } + + // Set clock pin low + gpio_init(PIN_CLOCK); + gpio_set_dir(PIN_CLOCK, GPIO_OUT); + gpio_put(PIN_CLOCK, 0); +} + +bool fpga_is_configured(void) +{ + return gpio_get(PIN_FPGA_CDONE); +} + +void fpga_reset(void) +{ + // Stop clock during reset + fpga_stop_clock(); + + // Pull reset low + gpio_put(PIN_FPGA_CRESETN, 0); + sleep_ms(10); // Hold reset for 10ms + + // Release reset + gpio_put(PIN_FPGA_CRESETN, 1); + +#ifdef BUILD_PICO2_ICE + // For pico2-ice: Start clock immediately due to CDONE voltage level hardware bug + sleep_ms(10); // Give FPGA time to start configuration + fpga_start_clock(); + + // Wait for configuration with timeout (though CDONE reading is unreliable) + uint32_t timeout_count = 0; + const uint32_t timeout_limit = 100; + + while (!gpio_get(PIN_FPGA_CDONE) && timeout_count < timeout_limit) { + sleep_ms(1); + timeout_count++; + } + +#else // BUILD_PICO_ICE + // Wait for configuration to complete + uint32_t timeout_count = 0; + const uint32_t timeout_limit = 100; + + while (!gpio_get(PIN_FPGA_CDONE) && timeout_count < timeout_limit) { + sleep_ms(1); + timeout_count++; + } + + // Restart clock if configuration succeeded + if (gpio_get(PIN_FPGA_CDONE)) { + fpga_start_clock(); + } +#endif +} + +#endif // defined(BUILD_PICO_ICE) || defined(BUILD_PICO2_ICE) \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.h b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.h new file mode 100644 index 00000000..92ce8bce --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_FPGA.h @@ -0,0 +1,56 @@ +#ifndef __LOGICANALYZER_FPGA__ +#define __LOGICANALYZER_FPGA__ + +#include "pico/stdlib.h" +#include "LogicAnalyzer_Board_Settings.h" + +#if defined(BUILD_PICO_ICE) || defined(BUILD_PICO2_ICE) + +/** + * @brief Initialize the FPGA subsystem for pico-ice + * + * This function performs the complete FPGA initialization sequence: + * 1. Configure all FPGA control pins + * 2. Release ICE_RESET to start FPGA configuration from flash + * 3. Wait for CDONE to be asserted + * 4. Start the 10MHz clock generation using PIO + * + * @return true if initialization successful, false if failed + */ +bool fpga_init(void); + +/** + * @brief Start the 10MHz clock output to the FPGA + * + * Uses PIO to generate a precise 10MHz clock signal on PIN_CLOCK (GPIO24). + * The frequency can be adjusted by modifying the divider calculation. + * + * @return true if clock started successfully, false if failed + */ +bool fpga_start_clock(void); + +/** + * @brief Stop the FPGA clock output + * + * Stops the PIO clock generation and sets the clock pin to low. + */ +void fpga_stop_clock(void); + +/** + * @brief Check if FPGA configuration is complete + * + * @return true if CDONE is asserted (FPGA configured), false otherwise + */ +bool fpga_is_configured(void); + +/** + * @brief Reset the FPGA + * + * Pulls ICE_RESET low, waits briefly, then releases it to restart + * the FPGA configuration process. + */ +void fpga_reset(void); + +#endif // defined(BUILD_PICO_ICE) || defined(BUILD_PICO2_ICE) + +#endif // __LOGICANALYZER_FPGA__ \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Structs.h b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Structs.h index efac4c03..f11224a3 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Structs.h +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_Structs.h @@ -23,7 +23,7 @@ //Trigger value of the pattern trigger uint16_t triggerValue; //Channels to capture - uint8_t channels[24]; + uint8_t channels[32]; //Channel count uint8_t channelCount; //Sampling frequency @@ -33,7 +33,7 @@ //Number of samples stored after the trigger uint32_t postSamples; //Number of capture loops - uint8_t loopCount; + uint16_t loopCount; //Measure burst times uint8_t measure; //Capture mode (0 = 8 channel, 1 = 16 channel, 2 = 24 channel) diff --git a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_W2812.c b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_W2812.c index bb715f81..a01260b4 100644 --- a/Firmware/LogicAnalyzer_V2/LogicAnalyzer_W2812.c +++ b/Firmware/LogicAnalyzer_V2/LogicAnalyzer_W2812.c @@ -3,7 +3,7 @@ #ifdef WS2812_LED #include "pico/stdlib.h" - #include "LogicAnalyzer_w2812.h" + #include "LogicAnalyzer_W2812.h" #define LONG_START 52.0 * (MAX_FREQ / 100000000.0) #define SHORT_START 26.0 * (MAX_FREQ / 100000000.0) diff --git a/Firmware/LogicAnalyzer_V2/Shared_Buffers.h b/Firmware/LogicAnalyzer_V2/Shared_Buffers.h index a46fb98f..41bbb2ad 100644 --- a/Firmware/LogicAnalyzer_V2/Shared_Buffers.h +++ b/Firmware/LogicAnalyzer_V2/Shared_Buffers.h @@ -6,7 +6,12 @@ #include "Event_Machine.h" #include "hardware/flash.h" - #define FLASH_SETTINGS_OFFSET ((2048 * 1024) - FLASH_SECTOR_SIZE) + #if defined (CORE_TYPE_2) + #define FLASH_SETTINGS_OFFSET ((4096 * 1024) - FLASH_SECTOR_SIZE) + #else + #define FLASH_SETTINGS_OFFSET ((2048 * 1024) - FLASH_SECTOR_SIZE) + #endif + #define FLASH_SETTINGS_ADDRESS (XIP_BASE + FLASH_SETTINGS_OFFSET) volatile extern WIFI_SETTINGS wifiSettings; diff --git a/Firmware/LogicAnalyzer_V2/fpga_clock.pio b/Firmware/LogicAnalyzer_V2/fpga_clock.pio new file mode 100644 index 00000000..00e931b8 --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/fpga_clock.pio @@ -0,0 +1,8 @@ +.program fpga_clock +.side_set 1 ; use 1 sideset bit to drive the clock pin + +; Simple 50% duty cycle clock: toggle the pin every instruction +.wrap_target + nop side 1 ; pin high + nop side 0 ; pin low +.wrap diff --git a/Firmware/LogicAnalyzer_V2/pico-ice-readme.md b/Firmware/LogicAnalyzer_V2/pico-ice-readme.md new file mode 100644 index 00000000..9134c21f --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/pico-ice-readme.md @@ -0,0 +1,243 @@ +# Using the Logic Analyzer with pico-ice + +The **pico-ice** is an FPGA development board featuring the **ICE40UP5K FPGA** paired with the **RP2040** microcontroller. This guide explains how to use Dr. Gusman's Logic Analyzer firmware with the pico-ice board for debugging FPGA projects. + +## Overview + +The pico-ice Logic Analyzer firmware provides a seamless integration between FPGA development and logic analysis. The RP2040 handles both FPGA configuration management and high-speed logic capture, making it an ideal tool for FPGA debugging and verification. + +## Prerequisites + +### 1. FPGA Flash Programming Required + +**⚠️ IMPORTANT: You must program the FPGA flash memory before using the Logic Analyzer.** + +The pico-ice Logic Analyzer firmware does **NOT** program the FPGA flash. You need to: + +1. Program your FPGA bitstream to the onboard flash memory using standard pico-ice tools +2. Ensure your FPGA design is stored in flash and ready for configuration +3. Only then use the Logic Analyzer firmware + +**Programming Tools:** +- Use the official pico-ice SDK and tools for flash programming +- Refer to the [pico-ice documentation](https://pico-ice.tinyvision.ai/md_getting__started.html) for detailed programming instructions +- Popular tools include `iceprog`, `openFPGALoader`, or the pico-ice MicroPython utilities + +### 2. Hardware Requirements + +- pico-ice development board +- USB cable for connection to host computer +- paper clip to put the pico-ice in bootsel mode +- FPGA bitstream programmed in flash memory + +## How It Works + +### FPGA Initialization Sequence + +When the Logic Analyzer firmware starts: + +1. **Reset Release**: The RP2040 pulls `CRESETN` (GPIO27) high, releasing the FPGA from reset +2. **Clock Start**: The RP2040 immediately starts a **10MHz clock** on `PIN_CLOCK` (GPIO24) using PIO for improved reliability +3. **Configuration Loading**: The ICE40UP5K automatically loads its configuration from onboard flash memory +4. **Configuration Complete**: The FPGA asserts `CDONE` (GPIO26) when configuration is successful (monitored for timeout but not required) +5. **Logic Analysis Ready**: The Logic Analyzer is now ready to capture signals while maintaining FPGA operation + +### Improved ReliabilitCompley + +**Note**: The firmware uses an **immediate clock start** approach for improved reliability. Instead of waiting for CDONE assertion (which can have voltage level issues), the FPGA clock starts immediately after reset release. This ensures consistent operation while still monitoring CDONE for timeout detection. + +### Dual Functionality + +The firmware provides **dual functionality**: +- **FPGA Support**: Manages FPGA configuration, reset control, and clock generation +- **Logic Analysis**: Captures high-speed digital signals from GPIO pins for analysis + +## Pin Configuration + +### FPGA Control Pins +- **GPIO27** (`CRESETN`) - FPGA reset control (active-low) - **OUTPUT** +- **GPIO26** (`CDONE`) - FPGA configuration done status - **INPUT** +- **GPIO24** (`PIN_CLOCK`) - FPGA clock output (10MHz) - **OUTPUT** + +### SPI Flash Programming Pins +- **GPIO8** (`ICE_SI`) - SPI MOSI to FPGA +- **GPIO11** (`ICE_SO`) - SPI MISO from FPGA +- **GPIO10** (`ICE_SCK`) - SPI clock +- **GPIO9** (`ICE_SSN`) - SPI chip select (active-low) +- **GPIO14** (`RAM_SS`) - External PSRAM chip select + +### Logic Analyzer Capture Pins and Channel Mapping + +The Logic Analyzer can monitor these GPIO pins: +- **GPIO0-GPIO7** - General purpose I/O +- **GPIO12-GPIO27** - Extended I/O range + +**Channel to GPIO Pin Mapping:** +``` +Logic Analyzer Channel → GPIO Pin +Channel 01 → GPIO0 +Channel 02 → GPIO1 +Channel 03 → GPIO2 +Channel 04 → GPIO3 +Channel 05 → GPIO4 +Channel 06 → GPIO5 +Channel 07 → GPIO6 +Channel 08 → GPIO7 +Channel 09 → GPIO12 +Channel 10 → GPIO13 +Channel 11 → GPIO14 +Channel 12 → GPIO15 +Channel 13 → GPIO20 +Channel 14 → GPIO21 +Channel 15 → GPIO22 +Channel 16 → GPIO23 +Channel 17 → GPIO24 (FPGA Clock) 🕐 +Channel 18 → GPIO25 +Channel 19 → GPIO26 (CDONE) 📡 +Channel 20 → GPIO27 (CRESETN) 🔄 + +Note: 🕐 = 10MHz FPGA clock output +📡 = FPGA configuration done status +🔄 = FPGA reset control (active-low) +``` +**Complex Triggering** +For complex trigger (pattern trigger) to work you need to short GPIO0 to GPIO1. You can change which GPIO to short in LogicAnalyzer_Board_Settings.h in the BUILD_PICO_ICE section. + +**Important Notes:** +- **Channels 17, 19, 20** (GPIO24, 26, 27) have special FPGA functions but can still be monitored +- **GPIO24** shows the 10MHz FPGA clock - useful for timing reference +- **GPIO26** shows FPGA configuration status (high when FPGA is configured) +- **GPIO27** shows FPGA reset control (should stay high during normal operation) + +**Note**: GPIO24 and GPIO27 are **output pins** but can still be **monitored** by the Logic Analyzer. The firmware uses universal pin state preservation to maintain their output state while allowing signal capture. **All pin configurations remain unchanged** after capture operations. + +### Status LEDs +- **GPIO13** - Red LED (active-low) +- **GPIO12** - Green LED (active-low) +- **GPIO15** - Blue LED (active-low) + +## Clock Configuration + +The FPGA receives a **10MHz clock** generated by the RP2040's PIO system. + +### Changing the FPGA Clock Frequency + +To modify the clock frequency, edit the `fpga_start_clock()` function in `LogicAnalyzer_FPGA.c`: + +```c +void fpga_start_clock() { + // Calculate divider for desired frequency + // Current: 10MHz = system_clock / divider + float divider = (float)clock_get_hz(clk_sys) / 10000000.0f; // 10MHz + + // For different frequencies: + // 5MHz: float divider = (float)clock_get_hz(clk_sys) / 5000000.0f; + // 25MHz: float divider = (float)clock_get_hz(clk_sys) / 25000000.0f; + + pio_sm_set_clkdiv(pio1, fpga_clock_sm, divider); +} +``` + +## Usage Instructions + +### 1. Build and Flash the Firmware + +```bash +# Configure for pico-ice +cmake -DBOARD_TYPE=BOARD_PICO_ICE .. +make -j8 + +# Flash the resulting .uf2 file to your pico-ice +``` + +### 2. Connect to Logic Analyzer Software + +1. Connect the pico-ice to your computer via USB +2. The device will identify as "Logic Analyzer (pico-ice)" +3. Use Dr. Gusman's Logic Analyzer software to connect +4. The software communicates via USB CDC using escaped binary protocol + +### 3. Verify FPGA Operation + +Before capturing signals: +1. Check that your FPGA configuration loaded successfully +2. Verify the 10MHz clock is present on GPIO24 +3. Confirm CRESETN (GPIO27) is at 3.3V +4. Ensure CDONE (GPIO26) is high + +### 4. Capture Logic Signals + +- Configure capture channels in the Logic Analyzer software +- You can monitor both FPGA I/O and RP2040 GPIO pins +- GPIO24 (FPGA clock) and GPIO27 (CRESETN) can be captured while maintaining their output functions + +## Advanced Features + +### Multi-Core Architecture + +The pico-ice Logic Analyzer uses both RP2040 cores: +- **Core 0**: Main Logic Analyzer functionality, USB communication, FPGA management +- **Core 1**: Available for user applications + +**Core 1 is available for custom user code!** You can implement additional functionality on Core 1 while the Logic Analyzer runs on Core 0. + +### Universal Pin State Preservation + +The firmware implements complete pin state preservation: +- **All pin states are preserved** during and after Logic Analyzer capture operations +- **Output pins** (like FPGA clock) maintain their output state and continue driving +- **Input pins** maintain their input configuration and pull-up/pull-down settings +- **High-Z pins** remain in high-impedance state +- **Core 1 user applications** are completely unaffected by Logic Analyzer operations +- **FPGA applications** continue running without any pin state disruption +- Logic Analyzer only **reads** pin states - never changes pin configurations + +### Real-time FPGA Monitoring + +The Logic Analyzer can capture FPGA signals in real-time while the FPGA continues normal operation: +- Monitor FPGA I/O signals +- Debug timing relationships +- Capture FPGA clock for timing reference +- Analyze FPGA-to-RP2040 communication + +## Troubleshooting + +### FPGA Not Configuring +- Verify FPGA flash is programmed with valid bitstream +- Check that CRESETN (GPIO27) reaches 3.3V +- Ensure CDONE (GPIO26) goes high after configuration + +### No FPGA Clock +- Verify CDONE is high before expecting clock +- Check GPIO24 with oscilloscope for 10MHz signal +- Ensure Logic Analyzer capture isn't interfering with clock output + +### Logic Analyzer Connection Issues +- Verify USB connection and drivers +- Check that device identifies as "Logic Analyzer (pico-ice)" +- Ensure Logic Analyzer software supports the pico-ice variant + +## Technical Specifications + +- **MCU**: RP2040 (Dual ARM Cortex-M0+ @ 125MHz) +- **FPGA**: ICE40UP5K (5K LUTs, 128KB BRAM, 8 DSP blocks) +- **Logic Analyzer Channels**: Up to 24 channels (GPIO0-7, 12-27) +- **Max Sample Rate**: 100 Msps +- **Capture Memory**: 32KB buffer +- **FPGA Clock**: 10MHz (user configurable) +- **Communication**: USB CDC (escaped binary protocol) + +## References + +- [pico-ice Official Documentation](https://pico-ice.tinyvision.ai/md_getting__started.html) +- [ICE40UP5K FPGA Datasheet](https://www.latticesemi.com/en/Products/FPGAandCPLD/iCE40UltraPlus) +- [Dr. Gusman's Logic Analyzer Project](https://github.com/gusmanb/logicanalyzer) +- [RP2040 Datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf) + +## Contributing + +This pico-ice support is designed to be merged with Dr. Gusman's main Logic Analyzer repository. Contributions, bug reports, and improvements are welcome! + +--- + +**Note**: This firmware provides FPGA development board support while maintaining full compatibility with the original Logic Analyzer functionality. The universal pin state preservation ensures reliable operation with any FPGA design and preserves all user application pin configurations. \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/pico2-ice-readme.md b/Firmware/LogicAnalyzer_V2/pico2-ice-readme.md new file mode 100644 index 00000000..1e52175f --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/pico2-ice-readme.md @@ -0,0 +1,265 @@ +# Using the Logic Analyzer with pico2-ice + +The **pico2-ice** is an FPGA development board featuring the **ICE40UP5K FPGA** paired with the **RP2350B** microcontroller. This guide explains how to use Dr. Gusman's Logic Analyzer firmware with the pico2-ice board for debugging FPGA projects. + +## Overview + +The pico2-ice Logic Analyzer firmware provides a seamless integration between FPGA development and logic analysis. The RP2350B handles both FPGA configuration management and high-speed logic capture, making it an ideal tool for FPGA debugging and verification. + +## Prerequisites + +### 1. FPGA Flash Programming Required + +**⚠️ IMPORTANT: You must program the FPGA flash memory before using the Logic Analyzer.** + +The pico2-ice Logic Analyzer firmware does **NOT** program the FPGA flash. You need to: + +1. Program your FPGA bitstream to the onboard flash memory using standard pico2-ice tools +2. Ensure your FPGA design is stored in flash and ready for configuration +3. Only then use the Logic Analyzer firmware + +**Programming Tools:** +- Use the official pico2-ice SDK and tools for flash programming +- Refer to the [pico2-ice documentation](https://pico2-ice.tinyvision.ai/md_getting__started.html) for detailed programming instructions +- Popular tools include `iceprog`, `openFPGALoader`, or the pico2-ice MicroPython utilities + +### 2. Hardware Requirements + +- pico2-ice development board +- USB cable for connection to host computer +- FPGA bitstream programmed in flash memory + +## How It Works + +### FPGA Initialization Sequence + +When the Logic Analyzer firmware starts: + +1. **Reset Release**: The RP2350B pulls `CRESETN` (GPIO31) high, releasing the FPGA from reset +2. **Clock Start**: Due to a hardware bug with CDONE voltage levels, the RP2350B immediately starts the **10MHz clock** on `PIN_CLOCK` (GPIO21) using PIO +3. **Configuration Loading**: The ICE40UP5K automatically loads its configuration from onboard flash memory +4. **Configuration Complete**: The FPGA asserts `CDONE` (GPIO40) when configuration is successful (though voltage levels may not be reliably readable) +5. **Logic Analysis Ready**: The Logic Analyzer is now ready to capture signals while maintaining FPGA operation + +### CDONE Hardware Bug Workaround + +**Note**: The pico2-ice has a hardware issue where the CDONE signal voltage is below the RP2350B's input threshold. The firmware works around this by: +- Starting the FPGA clock immediately after releasing reset +- Not waiting for CDONE assertion (since it's not reliably readable) +- Providing sufficient time for FPGA configuration to complete + +### Dual Functionality + +The firmware provides **dual functionality**: +- **FPGA Support**: Manages FPGA configuration, reset control, and clock generation +- **Logic Analysis**: Captures high-speed digital signals from GPIO pins for analysis + +## Pin Configuration + +### FPGA Control Pins +- **GPIO31** (`CRESETN`) - FPGA reset control (active-low) - **OUTPUT** +- **GPIO40** (`CDONE`) - FPGA configuration done status (unreliable voltage) - **INPUT** +- **GPIO21** (`PIN_CLOCK`) - FPGA clock output (10MHz) - **OUTPUT** + +### SPI Flash Programming Pins +- **GPIO4** (`ICE_SI`) - SPI MOSI to FPGA +- **GPIO7** (`ICE_SO`) - SPI MISO from FPGA +- **GPIO6** (`ICE_SCK`) - SPI clock +- **GPIO5** (`ICE_SSN`) - SPI chip select (active-low) +- **No external PSRAM** (PIN_RAM_SS = -1) + +### Logic Analyzer Capture Pins and Channel Mapping + +The Logic Analyzer can monitor these GPIO pins: +- **GPIO20-GPIO43** - High-speed I/O range (excluding conflicting pins) + +**Channel to GPIO Pin Mapping:** +``` +Logic Analyzer Channel → GPIO Pin +Channel 01 → GPIO20 ICE_27 +Channel 02 → GPIO21 (FPGA Clock) 🕐 +Channel 03 → GPIO22 ICE_20_G3 +Channel 04 → GPIO23 ICE_19 +Channel 05 → GPIO24 ICE_26 +Channel 06 → GPIO25 ICE_23 +Channel 07 → GPIO26 ICE_21 +Channel 08 → GPIO27 ICE_18 +Channel 09 → GPIO28 ICE_9 +Channel 10 → GPIO29 ICE_11 +Channel 11 → GPIO30 ICE_25 +Channel 12 → GPIO31 (CRESETN) 🔄 +Channel 13 → GPIO32 +Channel 14 → GPIO33 +Channel 15 → GPIO34 +Channel 16 → GPIO35 +Channel 17 → GPIO36 +Channel 18 → GPIO37 +Channel 19 → GPIO38 +Channel 20 → GPIO39 +Channel 21 → GPIO40 (CDONE) 📡 +Channel 22 → GPIO41 +Channel 23 → GPIO42 +Channel 24 → GPIO43 + +Note: 🕐 = 10MHz FPGA clock output +📡 = FPGA configuration done status (unreliable voltage) +🔄 = FPGA reset control (active-low) + +``` +**Complex Triggering** +Note: GPIO2 and GPIO3 must be jumpered if you want to use Complex Triggering (triggering on a pattern). You can change these LogicAnalyzer_Board_Settings.h if you want other pins on the RP2350B.` +**Important Notes:** +- **Channel 02** (GPIO21) shows the 10MHz FPGA clock - useful for timing reference +- **Channel 12** (GPIO31) shows FPGA reset control (should stay high during normal operation) +- **Channel 21** (GPIO40) shows FPGA configuration status (voltage may be unreliable) + +**Note**: GPIO21 and GPIO31 are **output pins** but can still be **monitored** by the Logic Analyzer. The firmware uses universal pin state preservation to maintain their output state while allowing signal capture. **All pin configurations remain unchanged** after capture operations. + +### Status LEDs (RGB LED) +- **GPIO1** - Red LED (active-low) +- **GPIO0** - Green LED (active-low) +- **GPIO9** - Blue LED (active-low) + +## Clock Configuration + +The FPGA receives a **10MHz clock** generated by the RP2350B's PIO system. + +### Changing the FPGA Clock Frequency + +To modify the clock frequency, edit the `fpga_start_clock()` function in `LogicAnalyzer_FPGA.c`: + +```c +bool fpga_start_clock(void) { + // Calculate divider for desired frequency + float target_freq = 10000000.0f; // 10MHz + + // For different frequencies: + // 5MHz: float target_freq = 5000000.0f; + // 25MHz: float target_freq = 25000000.0f; + + float system_freq = (float)clock_get_hz(clk_sys); + float clkdiv = system_freq / (target_freq * 2.0f); + sm_config_set_clkdiv(&c, clkdiv); +} +``` + +## Usage Instructions + +### 1. Build and Flash the Firmware + +```bash +# Configure for pico2-ice +cmake -DBOARD_TYPE=BOARD_PICO2_ICE .. +make -j8 + +# Flash the resulting .uf2 file to your pico2-ice +``` + +### 2. Connect to Logic Analyzer Software + +1. Connect the pico2-ice to your computer via USB +2. The device will identify as "Logic Analyzer (PICO2_ICE)" +3. Use Dr. Gusman's Logic Analyzer software to connect +4. The software communicates via USB CDC using escaped binary protocol + +### 3. Verify FPGA Operation + +Before capturing signals: +1. Check that your FPGA configuration loaded successfully (by observing FPGA I/O behavior) +2. Verify the 10MHz clock is present on GPIO21 (Channel 02) +3. Confirm CRESETN (GPIO31) is at 3.3V (Channel 12) +4. Note that CDONE (GPIO40) voltage may not be reliably readable due to hardware bug + +### 4. Capture Logic Signals + +- Configure capture channels in the Logic Analyzer software +- You can monitor both FPGA I/O and RP2350B GPIO pins +- GPIO21 (FPGA clock) and GPIO31 (CRESETN) can be captured while maintaining their output functions + +## Advanced Features + +### Multi-Core Architecture + +The pico2-ice Logic Analyzer uses both RP2350B cores: +- **Core 0**: Main Logic Analyzer functionality, USB communication, FPGA management +- **Core 1**: Available for user applications + +**Core 1 is available for custom user code!** You can implement additional functionality on Core 1 while the Logic Analyzer runs on Core 0. + +### Universal Pin State Preservation + +The firmware implements complete pin state preservation: +- **All pin states are preserved** during and after Logic Analyzer capture operations +- **Output pins** (like FPGA clock) maintain their output state and continue driving +- **Input pins** maintain their input configuration and pull-up/pull-down settings +- **High-Z pins** remain in high-impedance state +- **Core 1 user applications** are completely unaffected by Logic Analyzer operations +- **FPGA applications** continue running without any pin state disruption +- Logic Analyzer only **reads** pin states - never changes pin configurations + +### Real-time FPGA Monitoring + +The Logic Analyzer can capture FPGA signals in real-time while the FPGA continues normal operation: +- Monitor FPGA I/O signals +- Debug timing relationships +- Capture FPGA clock for timing reference +- Analyze FPGA-to-RP2350B communication + +## Hardware Differences from pico-ice + +### Key Changes in pico2-ice: +- **MCU**: RP2350B (80-pin package) instead of RP2040 +- **GPIO Range**: Uses GPIO20-43 instead of GPIO0-27 +- **FPGA Pins**: Different GPIO assignments (CRESETN=GPIO31, CLOCK=GPIO21, CDONE=GPIO40) +- **PSRAM**: No external PSRAM (PIN_RAM_SS = -1) +- **CDONE Bug**: Hardware voltage level issue requires immediate clock start workaround +- **RGB LED**: Single RGB LED on GPIO0/1/9 instead of separate LEDs + +## Troubleshooting + +### FPGA Not Configuring +- Verify FPGA flash is programmed with valid bitstream +- Check that CRESETN (GPIO31) reaches 3.3V +- The FPGA clock starts immediately - don't rely on CDONE signal due to voltage bug +- Allow sufficient time (1000ms) for FPGA configuration to complete + +### No FPGA Clock +- Clock starts immediately after reset release (doesn't wait for CDONE) +- Check GPIO21 with oscilloscope for 10MHz signal +- Ensure Logic Analyzer capture isn't interfering with clock output + +### CDONE Signal Issues +- **Known Hardware Bug**: CDONE voltage may not reach proper logic HIGH level +- Don't rely on CDONE for determining FPGA configuration status +- Use FPGA I/O behavior to verify successful configuration instead + +### Logic Analyzer Connection Issues +- Verify USB connection and drivers +- Check that device identifies as "Logic Analyzer (PICO2_ICE)" +- Ensure Logic Analyzer software supports the pico2-ice variant + +## Technical Specifications + +- **MCU**: RP2350B (Dual ARM Cortex-M33 @ 125MHz, 80-pin package) +- **FPGA**: ICE40UP5K (5K LUTs, 128KB BRAM, 8 DSP blocks) +- **Logic Analyzer Channels**: 24 channels (GPIO20-43) +- **Max Sample Rate**: 100 Msps +- **Capture Memory**: 384KB buffer (3x larger than RP2040 boards) +- **FPGA Clock**: 10MHz (user configurable) +- **Communication**: USB CDC (escaped binary protocol) +- **Special Features**: Immediate clock start (CDONE bug workaround) + +## References + +- [pico2-ice Official Documentation](https://pico2-ice.tinyvision.ai/md_getting__started.html) +- [ICE40UP5K FPGA Datasheet](https://www.latticesemi.com/en/Products/FPGAandCPLD/iCE40UltraPlus) +- [Dr. Gusman's Logic Analyzer Project](https://github.com/gusmanb/logicanalyzer) +- [RP2350B Datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) + +## Contributing + +This pico2-ice support is designed to be merged with Dr. Gusman's main Logic Analyzer repository. Contributions, bug reports, and improvements are welcome! + +--- + +**Note**: This firmware provides FPGA development board support while maintaining full compatibility with the original Logic Analyzer functionality. The immediate clock start workaround ensures reliable FPGA operation despite the CDONE hardware bug, and universal pin state preservation ensures reliable operation with any FPGA design. \ No newline at end of file diff --git a/Firmware/LogicAnalyzer_V2/prompt_2025_9_25.md b/Firmware/LogicAnalyzer_V2/prompt_2025_9_25.md new file mode 100644 index 00000000..24695456 --- /dev/null +++ b/Firmware/LogicAnalyzer_V2/prompt_2025_9_25.md @@ -0,0 +1,33 @@ +# Goal +We wish to create a working pull request for El Dr. Gusman's LogicAnalyzer for the pico2-ice. We have a working branch (new_pico_ice). The pico-ice and pico2-ice are FPGA development boards that use the RP2040 and RP2350B respectively to control the ICE40UP5K FPGA. For FPGA projects the RP2040 and RP2350B will act as logic analyzers to help debug FPGA code. The pico-ice and pico2-ice supply the clock for the FPGA. We will load the FPGA flash and when the MCU pulls the CRESETN high the FPGA will be automatically programmed from the flash. There are two cores on the MCUs. I wish to use core0 for the logic analyzer leaving core1 for the user. Since we cannot know what the other user will program in core1, we would like to leave his GPIO pins as he set them (and he might set them with PIO or CPU) for the logic analyzer capture. That way the logic analyzer does not interfere with his code. This method is presently working fine for the pico-ice. + +I wish to start from the new_pico_ice branch, make a new branch called new_pico2_ice and add the absolute minimum changes to it to enable the pico2-ice to work in the same way. + +# Prime Directive +Only make MINIMAL changes. This makes it easier for a pull request to be accepted. +I am hoping very little needs to be done to the code except for LogicAnalyzer_Board_Settings.h. I also want to stay in the style as much as I can of the original project. + + +## Notes for the pico2-ice +The pico2-ice documentation is at: https://pico2-ice.tinyvision.ai/md_getting__started.html I am including the ICE40UP5K FPGA datasheet converted from pdf to text (Firmware/LogicAnalyzer_V2/FPGA-dS.txt) for you to refer to. +- The RP2350B requires slightly different pico-sdk settings than the RP2350 and especially the RP2040 because it is the 80 pin package of the newer processor. +- Normally we would start the FPGA clock after CDONE is asserted. Unfortunately, due to a hardware bug on the pico2-ice, the voltage on the MCU input for CDONE is not > V_IH when CDONE is asserted high. This makes it so the RP2350B cannot read it. I suggest a work around of just starting the FPGA clock immediately, or if necessary, reading the ADC, and checking if the level is greater than 1.8V. The pico-ice is close to having this same hardware issue, and so in the new repository, we could implement the same fix for both boards, once it is known to work for the pico2-ice. + + +### Pinout for pico2-ice + +GPIO4 = PIN_ICE_SI (ICE_SI, RP2350 ➜ FPGA) +GPIO7 = PIN_ICE_SO (ICE_SO, FPGA ➜ RP2350) +GPIO6 = PIN_ICE_SCK (ICE_SCK) +GPIO5 = PIN_ICE_SSN (sysCONFIG SS, active-low) +GPIO31 = PIN_FPGA_CRESETN (CRESET_B, active-low) +GPIO40 = PIN_FPGA_CDONE (CDONE) +GPIO21 = PIN_CLOCK (clock to FPGA) +GPIO1/0/9 = LED_R/G/B (active-low) +No external PSRAM (PIN_RAM_SS = -1) + +pico2-ice pins to capture data from: +GPIO20-GPIO43 + +## Build +To change the board, all I should have to do is edit Logic_Analyzer_Build_Settings.cmake with the new board. For development, let's leave TURBO_MODE as 0. diff --git a/Firmware/LogicAnalyzer_V2/publish.ps1 b/Firmware/LogicAnalyzer_V2/publish.ps1 index 4ad6aa95..b72eabf7 100644 --- a/Firmware/LogicAnalyzer_V2/publish.ps1 +++ b/Firmware/LogicAnalyzer_V2/publish.ps1 @@ -1,20 +1,25 @@ param ( [Parameter(Mandatory=$true)] - [string]$packageName = "LogicAnalyzer" + [string]$packageVersion ) +$packageName = "logicanalyzer_" + $packageVersion + # Define board types and turbo mode options -$boardTypes = @("BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2") +$boardTypes = @("BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2", "BOARD_PICO_2_W", "BOARD_PICO_2_W_WIFI", "BOARD_INTERCEPTOR") $turboModes = @("0", "1") # Path to the build settings file $buildSettingsFile = "LogicAnalyzer_Build_Settings.cmake" # Paths from settings.json -$cmakePath = "${env:USERPROFILE}/.pico-sdk/cmake/v3.28.6/bin/cmake" -$ninjaPath = "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja" -$picoSdkPath = "${env:USERPROFILE}/.pico-sdk/sdk/2.0.0" -$picoToolchainPath = "${env:USERPROFILE}/.pico-sdk/toolchain/13_2_Rel1" +$cmakePath = "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake" +$cmakeBinPath = "${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin/cmake" +$ninjaPath = "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1" +$picoSdkPath = "${env:USERPROFILE}/.pico-sdk/sdk/2.1.1" +$picoToolchainPath = "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1" +$picoToolchainBinPath = "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin" +$picoToolPath = "${env:USERPROFILE}/.pico-sdk/picotool/2.1.1/picotool" # Function to update the build settings file function Update-BuildSettings { @@ -40,6 +45,18 @@ if (-Not (Test-Path -Path $publishDir)) { Remove-Item -Recurse -Force "$publishDir\*" } +# Set environment variables +$env:PICO_SDK_PATH = $picoSdkPath +$env:PICO_TOOLCHAIN_PATH = $picoToolchainPath + +# Add paths to $env:Path only if they are not already set +$pathsToAdd = @($picoToolchainBinPath, $picoToolPath, $cmakeBinPath, $ninjaPath) +foreach ($path in $pathsToAdd) { + if (-not ($path -in ($env:Path -split ";" | ForEach-Object { $_.Trim() }))) { + $env:Path = "$path;$env:Path" + } +} + # Loop through each board type and turbo mode combination foreach ($boardType in $boardTypes) { foreach ($turboMode in $turboModes) { @@ -56,11 +73,6 @@ foreach ($boardType in $boardTypes) { New-Item -ItemType Directory -Path "build" Set-Location -Path "build" - # Set environment variables - $env:PICO_SDK_PATH = $picoSdkPath - $env:PICO_TOOLCHAIN_PATH = $picoToolchainPath - $env:Path = "${env:USERPROFILE}/.pico-sdk/toolchain/13_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.0.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.28.6/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:Path}" - # Run the CMake configuration command & $cmakePath -G "Ninja" .. diff --git a/Software/LogicAnalyzer/Artwork/Logo.png b/Software/LogicAnalyzer/Artwork/Logo.png index 4bb9e98a..ca3c545a 100644 Binary files a/Software/LogicAnalyzer/Artwork/Logo.png and b/Software/LogicAnalyzer/Artwork/Logo.png differ diff --git a/Software/LogicAnalyzer/Artwork/Logo6.psd b/Software/LogicAnalyzer/Artwork/Logo6.psd index de7c2964..29109686 100644 Binary files a/Software/LogicAnalyzer/Artwork/Logo6.psd and b/Software/LogicAnalyzer/Artwork/Logo6.psd differ diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo.png b/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo.png index 4bb9e98a..ca3c545a 100644 Binary files a/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo.png and b/Software/LogicAnalyzer/LogicAnalyzer/Assets/Logo.png differ diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Classes/ProfilesSet.cs b/Software/LogicAnalyzer/LogicAnalyzer/Classes/ProfilesSet.cs new file mode 100644 index 00000000..f96eed3b --- /dev/null +++ b/Software/LogicAnalyzer/LogicAnalyzer/Classes/ProfilesSet.cs @@ -0,0 +1,22 @@ +using LogicAnalyzer.SigrokDecoderBridge; +using SharedDriver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LogicAnalyzer.Classes +{ + public class ProfilesSet + { + public List Profiles { get; } = new List(); + } + + public class Profile + { + public required string Name { get; set; } + public CaptureSession? CaptureSettings { get; set; } + public SerializableDecodingTree? DecoderConfiguration { get; set; } + } +} diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs index eead978a..ab174925 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SamplePreviewer.axaml.cs @@ -25,7 +25,7 @@ public partial class SamplePreviewer : UserControl, ISampleDisplay public event EventHandler? PinnedChanged; bool pinned = false; - public bool Pinned { get { return pinned; } } + public bool Pinned { get { return pinned; } set { pinned = value; if (PinnedChanged != null) PinnedChanged(this, new PinnedEventArgs { Pinned = pinned }); } } public SamplePreviewer() { diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs index 781879e9..da19dc79 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SampleViewer.axaml.cs @@ -78,7 +78,7 @@ private void SampleViewer_PointerMoved(object? sender, PointerEventArgs e) if (interval != null) { - var text = $"State: {(interval.value ? "High" : "Low")}\nLength: {interval.duration.ToSmallTime()} ({interval.end - interval.start} samples)"; + var text = $"{chan.ToString()}\nState: {(interval.value ? "High" : "Low")}\nLength: {interval.duration.ToSmallTime()} ({interval.end - interval.start} samples)\nInferred frequency: {interval.duration.ToInferredFrequency()}"; if (ToolTip.GetTip(this)?.ToString() != text || !ToolTip.GetIsOpen(this)) { diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml index 78f9ebaa..ed2e0b62 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml @@ -9,12 +9,11 @@ x:Class="LogicAnalyzer.Controls.SigrokDecoderManager"> - - + - + diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml.cs index d56a2906..1bce109b 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderManager.axaml.cs @@ -11,6 +11,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Linq.Expressions; +using System.Text; using System.Threading; namespace LogicAnalyzer.Controls; @@ -32,6 +34,72 @@ public partial class SigrokDecoderManager : UserControl Timer? decodeTimer; + public SerializableDecodingTree DecodingTree + { + get { return GenerateDecodingTree().ToSerializable(); } + set { ReconstructTree(value); } + } + + private void ReconstructTree(SerializableDecodingTree value) + { + + pnlControls.Children.Clear(); + decoderOptions.Clear(); + + optCount = 0; + + txtFilter.Text = ""; + GenerateCategories(); + + foreach (var branch in value.Branches) + { + var dec = DecoderCategories.First(c => c.Name == "All").Decoders.FirstOrDefault(o => o.Id == branch.DecoderId); + + if (dec == null) + continue; + + SigrokDecoderOptions options = new SigrokDecoderOptions(this, AnalyzerColors.GetColor(optCount++)); + options.OptionsUpdated += Options_OptionsUpdated; + options.RemoveDecoder += Options_RemoveDecoder; + options.Decoder = dec; + options.UpdateChannels(channels); + + options.SetChannels(branch.Channels); + options.SetValues(branch.Options); + + pnlControls.Children.Add(options); + decoderOptions.Add(options); + + ReconstructBranch(branch, options); + } + } + + private void ReconstructBranch(SerializableDecodingBranch branch, SigrokDecoderOptions options) + { + foreach(var child in branch.Children) + { + var dec = DecoderCategories.First(c => c.Name == "All").Decoders.FirstOrDefault(o => o.Id == child.DecoderId); + + if (dec == null) + continue; + + SigrokDecoderOptions childOptions = new SigrokDecoderOptions(this, AnalyzerColors.GetColor(optCount++)); + childOptions.OptionsUpdated += Options_OptionsUpdated; + childOptions.RemoveDecoder += Options_RemoveDecoder; + childOptions.Decoder = dec; + childOptions.UpdateChannels(channels); + + childOptions.SetChannels(child.Channels); + childOptions.SetValues(child.Options); + childOptions.SetInput(options); + + pnlControls.Children.Add(childOptions); + decoderOptions.Add(childOptions); + + ReconstructBranch(child, childOptions); + } + } + public AnalyzerChannel[]? Channels { get { return channels; } @@ -61,6 +129,8 @@ public SigrokDecoderManager() private void TxtFilter_PointerPressed(object? sender, PointerPressedEventArgs e) { txtFilter.Text = ""; + txtFilter.IsEnabled = true; + txtFilter.Focus(); } private void TxtFilter_TextChanged(object? sender, TextChangedEventArgs e) @@ -185,7 +255,6 @@ private void Options_OptionsUpdated(object? sender, System.EventArgs e) EnqueueDecoding(); } - private void EnqueueDecoding() { if(_provider == null) diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderOptions.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderOptions.axaml.cs index 86c91984..2cbd1e70 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderOptions.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Controls/SigrokDecoderOptions.axaml.cs @@ -39,6 +39,7 @@ public SigrokDecoderBase? Decoder public ObservableCollection Channels { get; } = new ObservableCollection(); List channelSelectors = new List(); + List optionControls = new List(); Dictionary selectedChannels = new Dictionary(); public IEnumerable SelectedChannels { get { return selectedChannels.Values; } } @@ -63,7 +64,7 @@ private void CreateOptions() { channelSelectors.Clear(); pnlOptions.Children.Clear(); - + optionControls.Clear(); selectedChannels.Clear(); values.Clear(); @@ -372,6 +373,7 @@ private void CreateOptions() Grid.SetRow(optControl, row++); Grid.SetColumnSpan(optControl, 2); + optionControls.Add(optControl); } pnlOptions.Children.Add(gridOptions); @@ -546,6 +548,91 @@ public void UpdateChannels(IEnumerable? Channels) OptionsUpdated?.Invoke(this, EventArgs.Empty); } + public void SetValues(SigrokOptionValue[] Values) + { + foreach(var value in Values) + { + if (values.ContainsKey(value.OptionIndex)) + { + values[value.OptionIndex].Value = value.Value; + var control = optionControls.FirstOrDefault(c => (c.Tag as SigrokOption).Index == value.OptionIndex); + + if (control == null) + continue; + + switch (control) + { + case CheckBox ck: + ck.IsChecked = (bool)value.Value; + break; + + case TextBox tb: + tb.Text = value.Value?.ToString(); + break; + + case NumericUpDown nud: + nud.Value = Convert.ToDecimal(value.Value); + break; + + default: + (control as ComboBox)?.SetValue(ComboBox.SelectedItemProperty, value.Value); + break; + + + //case Check b: + // (control as CheckBox)?.SetValue(CheckBox.IsCheckedProperty, b); + // break; + + //case string s: + // (control as TextBox)?.SetValue(TextBox.TextProperty, s); + // break; + + //case int i: + // (control as NumericUpDown)?.SetValue(NumericUpDown.ValueProperty, i); + // break; + + //case double d: + // (control as NumericUpDown)?.SetValue(NumericUpDown.ValueProperty, (decimal)d); + // break; + + //default: + // (control as ComboBox)?.SetValue(ComboBox.SelectedItemProperty, option.Value); + // break; + } + } + } + } + + public void SetChannels(SigrokSelectedChannel[] Channels) + { + foreach (var channel in Channels) + { + var selector = channelSelectors.FirstOrDefault(c => (c.Tag as SigrokChannel)?.Index == channel.SigrokIndex); + + if (selector == null) + continue; + + int idx = channel.CaptureIndex + 1; + + if (idx >= selector.Items.Count) + continue; + + var aChannel = this.Channels[idx]; + + selector.SelectedItem = aChannel; + } + } + + public void SetInput(SigrokDecoderOptions? Input) + { + if (cbInputs == null) + return; + + var selected = sourceOptions?.FirstOrDefault(o => o.Options == Input); + + if (selected != null) + cbInputs.SelectedItem = selected; + } class CBOption { diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml index 0a338e33..2146de70 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml @@ -13,9 +13,12 @@ - ©2024 Agustín Giménez Bernad - Version 6.0preview - Visit project website + ©2025 Agustín Giménez Bernad + Version 6.5 + Licensed under GPLv3+ + Visit WebSite + Decoders from the LibSigrokDecode project. Licensed under GPLv3+ + LibSigrokDecode Github diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml.cs index a053cdd5..fdd2ca5f 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/AboutDialog.axaml.cs @@ -1,5 +1,7 @@ using Avalonia.Controls; +using Avalonia.Interactivity; using LogicAnalyzer.Extensions; +using System; using System.Diagnostics; using System.IO.Packaging; using System.Reflection; @@ -15,7 +17,21 @@ public AboutDialog() txtVersion.Text = $"Version {GetAppVersion()}"; btnLicense.Click += BtnLicense_Click; lnkWebSite.Click += LnkWebSite_Click; + lnkSigrok.Click += LnkSigrok_Click; } + + private async void LnkSigrok_Click(object? sender, RoutedEventArgs e) + { + try + { + OpenUrl("https://github.com/sigrokproject/libsigrokdecode"); + } + catch + { + await this.ShowError("Cannot open page.", "Cannot start the default browser. You can access the site at https://github.com/sigrokproject/libsigrokdecode"); + } + } + private async void LnkWebSite_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { try diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml index 9c473372..2558124c 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml @@ -94,7 +94,7 @@ Blast mode Burst mode Burst count: - + Measure delays diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs index 14beed73..d790f550 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/CaptureDialog.axaml.cs @@ -56,7 +56,21 @@ public CaptureDialog() rbTriggerTypeEdge.IsCheckedChanged += rbTriggerTypeEdge_CheckedChanged; nudFrequency.ValueChanged += NudFrequency_ValueChanged; ckBlast.IsCheckedChanged += ckBlast_CheckedChanged; - + nudBurstCount.ValueChanged += NudBurstCount_ValueChanged; + } + + private void NudBurstCount_ValueChanged(object? sender, NumericUpDownValueChangedEventArgs e) + { + if (nudBurstCount.Value > 254) + { + ckMeasure.IsChecked = false; + ckMeasure.IsEnabled = false; + } + else + { + if(rbTriggerTypeEdge.IsChecked == true) + ckMeasure.IsEnabled = true; + } } protected override void OnKeyDown(KeyEventArgs e) @@ -130,7 +144,7 @@ public void Initialize(AnalyzerDriverBase Driver) driver = Driver; InitializeControlArrays(driver.ChannelCount); InitializeParameters(); - LoadSettings(driver.DriverType); + LoadSettings(driver); CheckMode(); SetDriverMode(driver.DriverType); InitializeTooltips(); @@ -172,10 +186,6 @@ private void SetDriverMode(AnalyzerDriverType DriverType) { if (DriverType == AnalyzerDriverType.Multi) { - //pnlAllTriggers.Children.Remove(pnlPatternTrigger); - //pnlSingleTrigger.Children.Add(pnlPatternTrigger); - //pnlAllTriggers.IsVisible = false; - //pnlSingleTrigger.IsVisible = true; grdMainContainer.RowDefinitions = new RowDefinitions("3.7*,*"); rbTriggerTypeEdge.IsVisible = false; pnlEdge.IsVisible = false; @@ -197,7 +207,7 @@ protected override void OnOpened(EventArgs e) this.FixStartupPosition(); } - private StackPanel CreateChannelRow(int FirstChannel, List Selectors) + private StackPanel CreateChannelRow(int FirstChannel, List Selectors, int TotalChannels) { StackPanel panel = new StackPanel(); panel.Orientation = Avalonia.Layout.Orientation.Horizontal; @@ -210,7 +220,7 @@ private StackPanel CreateChannelRow(int FirstChannel, List Sele Button btnSelAll = new Button { FontFamily = font, Content = "", FontSize = 16 }; btnSelAll.SetValue(Grid.RowProperty, 0); - btnSelAll.Click += (s, e) => { foreach (var ch in captureChannels.Where(c => c.ChannelNumber >= FirstChannel && c.ChannelNumber < FirstChannel + 8)) ch.Enabled = true; }; + btnSelAll.Click += (s, e) => { foreach (var ch in captureChannels.Where(c => c.ChannelNumber >= FirstChannel && c.ChannelNumber < FirstChannel + 8 && c.IsEnabled)) ch.Enabled = true; }; btnSelAll.Margin = new Thickness(0, 0, 0, 0); btnSelAll.Padding = new Thickness(0, 0, 0, 0); btnSelAll.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch; @@ -220,7 +230,7 @@ private StackPanel CreateChannelRow(int FirstChannel, List Sele Button btnSelNone = new Button { FontFamily = font, Content = "", FontSize = 16 }; btnSelNone.SetValue(Grid.RowProperty, 1); - btnSelNone.Click += (s, e) => { foreach (var ch in captureChannels.Where(c => c.ChannelNumber >= FirstChannel && c.ChannelNumber < FirstChannel + 8)) ch.Enabled = false; }; + btnSelNone.Click += (s, e) => { foreach (var ch in captureChannels.Where(c => c.ChannelNumber >= FirstChannel && c.ChannelNumber < FirstChannel + 8 && c.IsEnabled)) ch.Enabled = false; }; btnSelNone.Margin = new Thickness(0, 0, 0, 0); btnSelNone.Padding = new Thickness(5, 0, 5, 0); btnSelNone.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch; @@ -230,7 +240,7 @@ private StackPanel CreateChannelRow(int FirstChannel, List Sele Button btnSelInv = new Button { FontFamily = font, Content = "", FontSize = 16 }; btnSelInv.SetValue(Grid.RowProperty, 2); - btnSelInv.Click += (s, e) => { foreach (var ch in captureChannels.Where(c => c.ChannelNumber >= FirstChannel && c.ChannelNumber < FirstChannel + 8)) ch.Enabled = !ch.Enabled; }; + btnSelInv.Click += (s, e) => { foreach (var ch in captureChannels.Where(c => c.ChannelNumber >= FirstChannel && c.ChannelNumber < FirstChannel + 8 && c.IsEnabled)) ch.Enabled = !ch.Enabled; }; btnSelInv.Margin = new Thickness(0, 0, 0, 0); btnSelInv.Padding = new Thickness(0, 0, 0, 0); btnSelInv.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch; @@ -263,6 +273,9 @@ private StackPanel CreateChannelRow(int FirstChannel, List Sele channel.Deselected += Channel_Deselected; channel.ChangeColor += Channel_ChangeColor; Selectors.Add(channel); + + if(FirstChannel + buc >= TotalChannels) + channel.IsEnabled = false; } return panel; @@ -273,7 +286,7 @@ private void InitializeControlArrays(int ChannelCount) List channels = new List(); for (int firstChan = 0; firstChan < ChannelCount; firstChan += 8) - pnlChannels.Children.Add(CreateChannelRow(firstChan, channels)); + pnlChannels.Children.Add(CreateChannelRow(firstChan, channels, ChannelCount)); int maxTrigger = Math.Min(24, ChannelCount); @@ -343,16 +356,18 @@ private void CheckMode() } - private void LoadSettings(AnalyzerDriverType DriverType) + private void LoadSettings(AnalyzerDriverBase Driver) { - settingsFile = $"cpSettings{DriverType}.json"; + var driverType = Driver.DriverType; + + settingsFile = $"cpSettings{driverType}.json"; CaptureSession? settings = AppSettingsManager.GetSettings(settingsFile); if (settings != null) { foreach (var channel in settings.CaptureChannels) { - if (channel.ChannelNumber >= captureChannels.Length) + if (channel.ChannelNumber >= captureChannels.Length || channel.ChannelNumber > Driver.ChannelCount) continue; captureChannels[channel.ChannelNumber].Enabled = true; @@ -373,7 +388,7 @@ private void LoadSettings(AnalyzerDriverType DriverType) ckBlast.IsChecked = false; } - if (DriverType != AnalyzerDriverType.Emulated) + if (driverType != AnalyzerDriverType.Emulated) { switch (settings.TriggerType) { @@ -458,6 +473,18 @@ private async void btnAccept_Click(object? sender, RoutedEventArgs e) int loops = (int)(((ckBurst.IsChecked ?? false) && (rbTriggerTypeEdge.IsChecked ?? false)) ? (nudBurstCount.Value ?? 1) - 1 : 0); bool measure = (ckBurst.IsChecked ?? false) && (ckMeasure.IsChecked ?? false); + if(measure && loops > 253) + { + await this.ShowError("Error", "Too many burst to measure, reduce the burst count to 254 or disable burst measurement."); + return; + } + + if (measure && (int)(nudPostSamples.Value ?? 0) < 100) + { + await this.ShowError("Error", "Postsamples too low, disable burst measurement or increase it to 100"); + return; + } + if (nudPreSamples.Value + (nudPostSamples.Value * (loops + 1)) > max) { await this.ShowError("Error", $"Total samples cannot exceed {max}."); diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml index 11b0cc84..1bc6015d 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml @@ -11,29 +11,29 @@ Background="#383838" CanResize="False" WindowStartupLocation="CenterOwner"> - Master: - - + Master: + + - Slave 1: - - + Slave 1: + + - Slave 2: - - + Slave 2: + + - Slave 3: - - + Slave 3: + + - Slave 4: - - + Slave 4: + + diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml.cs index 8763247b..65bef577 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Dialogs/MultiConnectDialog.axaml.cs @@ -121,10 +121,13 @@ private async void Drop_SelectionChanged(object? sender, SelectionChangedEventAr var value = dbs[index].SelectedItem?.ToString(); if (value == null) + { tbs[index].Text = ""; + dbs[index].Width = 255; + } else { - if(value == "Network") + if (value == "Network") { var dlg = new NetworkDialog(); var success = await dlg.ShowDialog(this); @@ -133,12 +136,19 @@ private async void Drop_SelectionChanged(object? sender, SelectionChangedEventAr { dbs[index].SelectedItem = null; tbs[index].Text = ""; + dbs[index].Width = 255; } else + { tbs[index].Text = $"{dlg.Address}:{dlg.Port}"; + dbs[index].Width = 110; + } } else + { tbs[index].Text = ""; + dbs[index].Width = 255; + } } } diff --git a/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs b/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs index b537ab40..994a8507 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/Extensions/DoubleExtensions.cs @@ -8,6 +8,10 @@ namespace LogicAnalyzer.Extensions { public static class DoubleExtensions { + public static string ToInferredFrequency(this double Time) + { + return (1.0 / (Time * 2)).ToLargeFrequency(); + } public static string ToSmallTime(this double Time) { if (Time < 0.000001) diff --git a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj index f685ada9..90384f55 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj +++ b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj @@ -8,7 +8,10 @@ true Assets\Ico40.ico True - 6.0.0.0 + 6.5.0.0 + (C) 2025 El Dr. Gusman + https://github.com/gusmanb/logicanalyzer + LogicAnalyzer @@ -37,7 +40,7 @@ - + diff --git a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user index 95c38da9..6586b64d 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user +++ b/Software/LogicAnalyzer/LogicAnalyzer/LogicAnalyzer.csproj.user @@ -1,8 +1,8 @@  - <_LastSelectedProfileId>F:\LogicAnalyzerRepo\Software\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\Linux.pubxml - WSL + <_LastSelectedProfileId>F:\LogicAnalyzerRepo\Software\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\Windows.pubxml + LogicAnalyzer ProjectDebugger diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml index bf73e4ac..2500f211 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml +++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml @@ -5,12 +5,11 @@ xmlns:controls="clr-namespace:LogicAnalyzer.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" x:Class="LogicAnalyzer.MainWindow" - Title="LogicAnalyzer 6.0" Icon="/Assets/Ico40.ico" Background="#383838" MinWidth="800" MinHeight="600" Width="800" Height="600" WindowStartupLocation="CenterScreen"> - + @@ -21,6 +20,8 @@ + + @@ -76,8 +77,8 @@ - - + + @@ -90,7 +91,7 @@ 10000 - + diff --git a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs index f696d901..d11d5dd1 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/MainWindow.axaml.cs @@ -12,6 +12,8 @@ using LogicAnalyzer.Dialogs; using LogicAnalyzer.Extensions; using LogicAnalyzer.Interfaces; +using LogicAnalyzer.SigrokDecoderBridge; +using MsBox.Avalonia; using Newtonsoft.Json; using SharedDriver; using SigrokDecoderBridge; @@ -35,6 +37,8 @@ namespace LogicAnalyzer { public partial class MainWindow : PersistableWindowBase { + const string Version = "LogicAnalyzer 6.5"; + AnalyzerDriverBase? driver; CaptureSession session; @@ -48,7 +52,6 @@ public partial class MainWindow : PersistableWindowBase AnalysisSettings? analysisSettings; //bool preserveSamples = false; - Timer tmrPower; Timer tmrHideSamples; List sampleDisplays = new List(); @@ -58,10 +61,23 @@ public partial class MainWindow : PersistableWindowBase List knownDevices = new List(); KnownDevice? currentKnownDevice = null; + ProfilesSet? profiles; + + public bool PreviewPinned { get { return samplePreviewer.Pinned; } set { samplePreviewer.Pinned = value; } } + + protected override string[]? PersistProperties + { + get + { + return ["PreviewPinned"]; + } + } + public MainWindow() { Instance = this; InitializeComponent(); + this.Title = Version; btnRefresh.Click += btnRefresh_Click; btnOpenClose.Click += btnOpenClose_Click; btnRepeat.Click += btnRepeat_Click; @@ -107,13 +123,7 @@ public MainWindow() mnuAbout.Click += MnuAbout_Click; AddHandler(InputElement.KeyDownEvent, MainWindow_KeyDown, handledEventsToo: true); - tmrPower = new Timer((o) => - { - Dispatcher.UIThread.InvokeAsync(() => - { - GetPowerStatus(); - }); - }); + pnlPower.PointerPressed += (o, e) => GetPowerStatus(); tmrHideSamples = new Timer((o) => { @@ -126,6 +136,12 @@ public MainWindow() this.Closed += (o, e) => { + if (driver != null && driver.IsCapturing) + { + driver.StopCapture(); + driver.Dispose(); + } + if(decoderProvider != null) decoderProvider.Dispose(); }; @@ -145,18 +161,165 @@ public MainWindow() Task.Run(() => LoadKnownDevices()); RefreshPorts(); + LoadProfiles(); + try { decoderProvider = new SigrokProvider(); sgManager.Initialize(decoderProvider); sgManager.DecodingComplete += SgManager_DecodingComplete; } - catch + catch (Exception ex) { _ = this.ShowError("Error loading decoders.", "Cannot load Sigrok decoders. Make sure Python is installed on your computer. If, despite being installed, you still have problems, you can specify the path to the Python library in \"python.cfg\"."); } } + private void LoadProfiles() + { + mnuProfiles.Items.Clear(); + + var addProfile = new MenuItem { Header = "Add profile" }; + addProfile.Click += AddProfile_Click; + mnuProfiles.Items.Add(addProfile); + mnuProfiles.Items.Add(new Separator()); + + profiles = AppSettingsManager.GetSettings("profiles.json"); + + if (profiles != null) + { + foreach(var profile in profiles.Profiles) + { + var mnuProfile = new MenuItem { Header = profile.Name }; + mnuProfiles.Items.Add(mnuProfile); + + var mnuLoad = new MenuItem { Header = "Load profile" }; + mnuLoad.Tag = profile; + mnuLoad.Click += MnuLoad_Click; + mnuProfile.Items.Add(mnuLoad); + + var mnuDelete = new MenuItem { Header = "Delete profile" }; + mnuDelete.Tag = profile; + mnuDelete.Click += MnuDelete_Click; + mnuProfile.Items.Add(mnuDelete); + } + + } + } + + private void SaveProfiles() + { + AppSettingsManager.PersistSettings("profiles.json", profiles ?? new ProfilesSet()); + } + + private async void MnuDelete_Click(object? sender, RoutedEventArgs e) + { + + var profile = (sender as MenuItem)?.Tag as Profile; + + if (profile == null) + return; + + if (await this.ShowConfirm("Delete profile", $"Are you sure you want to delete the profile \"{profile.Name}\"?")) + { + profiles?.Profiles.Remove(profile); + SaveProfiles(); + LoadProfiles(); + } + } + + private async void MnuLoad_Click(object? sender, RoutedEventArgs e) + { + + if (driver == null) + { + await this.ShowError("Load profile", "No device connected, cannot load profile."); + return; + } + + var mnu = sender as MenuItem; + + if (mnu == null) + return; + + var profile = mnu.Tag as Profile; + + if (profile == null) + return; + + if (driver.IsCapturing) + { + if (! await this.ShowConfirm("Load profile", "There is a capture in progress. Do you want to stop it and load the profile?")) + return; + + driver.StopCapture(); + } + + session = profile.CaptureSettings ?? new CaptureSession(); + updateChannels(true); + mnuSave.IsEnabled = false; + mnuExport.IsEnabled = false; + clearRegions(); + updateSamplesInDisplay(Math.Max(session.PreTriggerSamples - 10, 0), (int)tkInScreen.Value); + LoadInfo(); + + var settingsFile = $"cpSettings{driver.DriverType}.json"; + var settings = session.Clone(); + + foreach (var channel in settings.CaptureChannels) + channel.Samples = null; + + AppSettingsManager.PersistSettings(settingsFile, settings); + + sgManager.DecodingTree = profile.DecoderConfiguration ?? new SerializableDecodingTree(); + } + + private async void AddProfile_Click(object? sender, RoutedEventArgs e) + { + var dlg = MessageBoxManager.GetMessageBoxCustom(new MsBox.Avalonia.Dto.MessageBoxCustomParams + { + ButtonDefinitions = new List + { + new MsBox.Avalonia.Models.ButtonDefinition { Name = "Cancel", IsCancel = true, IsDefault = false }, + new MsBox.Avalonia.Models.ButtonDefinition { Name = "Save", IsCancel = false, IsDefault = true } + }, + InputParams = new MsBox.Avalonia.Dto.InputParams + { + Label = "New profile name:", Multiline = false + }, + Icon = MsBox.Avalonia.Enums.Icon.Setting, + ContentTitle = "Add profile", + ContentMessage = "Enter the name for the new profile.", + Width = 400, + ShowInCenter = true, + WindowStartupLocation = WindowStartupLocation.CenterOwner + }); + + var result = await dlg.ShowWindowDialogAsync(this); + + if(result == "Save") + { + var profileName = dlg.InputValue; + + if (string.IsNullOrWhiteSpace(profileName)) + return; + + if (profiles == null) + profiles = new ProfilesSet(); + + var profile = new Profile + { + Name = profileName, + CaptureSettings = session?.CloneSettings(), + DecoderConfiguration = sgManager.DecodingTree + }; + + profiles.Profiles.Add(profile); + SaveProfiles(); + LoadProfiles(); + } + } + private async void LblForget_PointerPressed(object? sender, PointerPressedEventArgs e) { if(currentKnownDevice != null) @@ -196,15 +359,7 @@ private async void LblBootloader_PointerPressed(object? sender, PointerPressedEv { driver.Dispose(); driver = null; - lblConnectedDevice.Text = "< None >"; - ddPorts.IsEnabled = true; - btnRefresh.IsEnabled = true; - btnOpenClose.Content = "Open device"; - RefreshPorts(); - btnCapture.IsEnabled = false; - btnRepeat.IsEnabled = false; - mnuSettings.IsEnabled = false; - tmrPower.Change(Timeout.Infinite, Timeout.Infinite); + syncUI(); await this.ShowInfo("Bootloader", "Device entered bootloader mode."); } else @@ -230,7 +385,7 @@ private async void LblInfo_PointerPressed(object? sender, PointerPressedEventArg private void ScrSamplePos_PointerWheelChanged(object? sender, PointerWheelEventArgs e) { - if(e.Delta.Y > 0) + if(e.Delta.Y < 0) { var currentVal = scrSamplePos.Value; int newVal = (int)(currentVal - scrSamplePos.Maximum / 20); @@ -240,7 +395,7 @@ private void ScrSamplePos_PointerWheelChanged(object? sender, PointerWheelEventA updateSamplesInDisplay(newVal, (int)tkInScreen.Value); } - else if (e.Delta.Y < 0) + else if (e.Delta.Y > 0) { var currentVal = scrSamplePos.Value; int newVal = (int)(currentVal + scrSamplePos.Maximum / 20); @@ -254,11 +409,11 @@ private void ScrSamplePos_PointerWheelChanged(object? sender, PointerWheelEventA private void SampleViewer_PointerWheelChanged(object? sender, PointerWheelEventArgs e) { - if (e.KeyModifiers == KeyModifiers.Shift) + if (e.KeyModifiers == KeyModifiers.Control) { e.Handled = true; - if (e.Delta.Y > 0) + if (e.Delta.Y < 0) { var currentVal = tkInScreen.Value; int newVal = (int)currentVal * 2; @@ -269,7 +424,7 @@ private void SampleViewer_PointerWheelChanged(object? sender, PointerWheelEventA updateSamplesInDisplay((int)scrSamplePos.Value, newVal); } - else if (e.Delta.Y < 0) + else if (e.Delta.Y > 0) { var currentVal = tkInScreen.Value; int newVal = (int)currentVal / 2; @@ -280,11 +435,11 @@ private void SampleViewer_PointerWheelChanged(object? sender, PointerWheelEventA updateSamplesInDisplay((int)scrSamplePos.Value, newVal); } } - else if (e.KeyModifiers == KeyModifiers.Control) + else if (e.KeyModifiers == KeyModifiers.Shift) { e.Handled = true; - if (e.Delta.Y > 0) + if (e.Delta.Y < 0) { var increment = tkInScreen.Value / 4.0; var currentValue = scrSamplePos.Value; @@ -295,7 +450,7 @@ private void SampleViewer_PointerWheelChanged(object? sender, PointerWheelEventA updateSamplesInDisplay((int)currentValue, (int)tkInScreen.Value); } - else if (e.Delta.Y < 0) + else if (e.Delta.Y > 0) { var increment = tkInScreen.Value / 4.0; var currentValue = scrSamplePos.Value; @@ -316,6 +471,12 @@ private void SamplePreviewer_PinnedChanged(object? sender, SamplePreviewer.Pinne tmrHideSamples.Change(Timeout.Infinite, Timeout.Infinite); samplePreviewer.IsVisible = true; grdContent.Margin = new Thickness(0, 0, 0, samplePreviewer.Bounds.Height); + + if (samplePreviewer.Bounds.Height == 0) + { + grdContent.Margin = new Thickness(0, 0, 0, samplePreviewer.Height); + } + } else { @@ -870,14 +1031,17 @@ private void UpdateSamples(int firstSample, int totalSamples, int finalPreSample { session.PreTriggerSamples = finalPreSamples; session.PostTriggerSamples = totalSamples - finalPreSamples; + session.Bursts = null; + session.MeasureBursts = false; sampleViewer.BeginUpdate(); - sampleViewer.PreSamples = 0; + //sampleViewer.PreSamples = 0; sampleViewer.Bursts = null; sampleViewer.EndUpdate(); samplePreviewer.UpdateSamples(channelViewer.Channels, session.TotalSamples); - samplePreviewer.ViewPosition = sampleViewer.FirstSample; + sampleViewer.SetChannels(channelViewer.Channels, session.Frequency); + //samplePreviewer.ViewPosition = firstSample; clearRegions(); @@ -929,7 +1093,12 @@ private async void MnuNetSettings_Click(object? sender, RoutedEventArgs e) if (await dlg.ShowDialog(this)) { - bool res = driver.SendNetworkConfig(dlg.AccessPoint, dlg.Password, dlg.Address, dlg.Port); + bool res = false; + try + { + res = driver.SendNetworkConfig(dlg.AccessPoint, dlg.Password, dlg.Address, dlg.Port); + } + catch { } if (!res) await this.ShowError("Error", "Error updating network settings, restart the device and try again."); @@ -1008,17 +1177,10 @@ private void Driver_CaptureCompleted(object? sender, CaptureEventArgs e) sampleViewer.Bursts = session.Bursts?.Select(b => b.BurstSampleStart).ToArray(); sampleMarker.Bursts = session.Bursts; - btnCapture.IsEnabled = true; - btnRepeat.IsEnabled = true; - btnOpenClose.IsEnabled = true; - btnAbort.IsEnabled = false; - mnuSave.IsEnabled = true; - mnuExport.IsEnabled = true; - - mnuSettings.IsEnabled = driver?.DriverType == AnalyzerDriverType.Serial && (driver.DeviceVersion?.Contains("WIFI") ?? false); + syncUI(); scrSamplePos.Maximum = session.TotalSamples - 1; - updateSamplesInDisplay(session.PreTriggerSamples - 2, Math.Max(session.PreTriggerSamples - 10, 0)); + updateSamplesInDisplay(session.PreTriggerSamples - 2, (int)tkInScreen.Value); LoadInfo(); GetPowerStatus(); @@ -1036,7 +1198,7 @@ private void ExtractSamples(AnalyzerChannel channel, int ChannelIndex, UInt128[] private async void btnOpenClose_Click(object? sender, EventArgs e) { - if (driver == null) + if (driver == null || driver is EmulatedAnalyzerDriver) { if (ddPorts.SelectedIndex == -1) { @@ -1067,13 +1229,6 @@ private async void btnOpenClose_Click(object? sender, EventArgs e) break; } - - if (driver != null) - { - driver.CaptureCompleted += Driver_CaptureCompleted; - lblBootloader.IsVisible = true; - lblInfo.IsVisible = true; - } } catch(Exception ex) { @@ -1083,15 +1238,8 @@ private async void btnOpenClose_Click(object? sender, EventArgs e) if (driver != null) { - lblConnectedDevice.Text = driver.DeviceVersion; - ddPorts.IsEnabled = false; - btnRefresh.IsEnabled = false; - btnOpenClose.Content = "Close device"; - btnCapture.IsEnabled = true; - btnRepeat.IsEnabled = true; - mnuSettings.IsEnabled = driver.DriverType == AnalyzerDriverType.Serial && (driver.DeviceVersion?.Contains("WIFI") ?? false); - tmrPower.Change(30000, Timeout.Infinite); driver.CaptureCompleted += Driver_CaptureCompleted; + syncUI(); } @@ -1100,18 +1248,8 @@ private async void btnOpenClose_Click(object? sender, EventArgs e) { driver.Dispose(); driver = null; - lblConnectedDevice.Text = "< None >"; - ddPorts.IsEnabled = true; - btnRefresh.IsEnabled = true; - btnOpenClose.Content = "Open device"; - RefreshPorts(); - btnCapture.IsEnabled = false; - btnRepeat.IsEnabled = false; - mnuSettings.IsEnabled = false; - tmrPower.Change(Timeout.Infinite, Timeout.Infinite); - lblBootloader.IsVisible = true; - lblInfo.IsVisible = true; currentKnownDevice = null; + syncUI(); } GetPowerStatus(); @@ -1312,7 +1450,7 @@ void RefreshPorts() ddPorts.ItemsSource = null; ddPorts.ItemsSource = portItems.ToArray(); - + ddPorts.SelectedIndex = 0; } @@ -1323,8 +1461,13 @@ private async void btnRepeat_Click(object? sender, RoutedEventArgs e) await this.ShowError("Error", "No capture to repeat"); return; } - //preserveSamples = true; - await BeginCapture(); + + if (!await BeginCapture()) + return; + + this.Title = Version; + + syncUI(); } private async void btnCapture_Click(object? sender, RoutedEventArgs e) @@ -1340,38 +1483,30 @@ private async void btnCapture_Click(object? sender, RoutedEventArgs e) return; session = dialog.SelectedSettings; - //preserveSamples = false; - - tmrPower.Change(Timeout.Infinite, Timeout.Infinite); - try - { - if(!await BeginCapture()) - return; + if(!await BeginCapture()) + return; - var settingsFile = $"cpSettings{driver.DriverType}.json"; - var settings = session.Clone(); + this.Title = Version; + + var settingsFile = $"cpSettings{driver.DriverType}.json"; + var settings = session.Clone(); - foreach(var channel in settings.CaptureChannels) - channel.Samples = null; + foreach(var channel in settings.CaptureChannels) + channel.Samples = null; - AppSettingsManager.PersistSettings(settingsFile, settings); + AppSettingsManager.PersistSettings(settingsFile, settings); + + syncUI(); - } - finally - { - tmrPower.Change(30000, Timeout.Infinite); - } } private void btnAbort_Click(object? sender, RoutedEventArgs e) { driver?.StopCapture(); - btnCapture.IsEnabled = true; - btnRepeat.IsEnabled = true; - btnOpenClose.IsEnabled = true; - btnAbort.IsEnabled = false; + + syncUI(); } private async Task BeginCapture() @@ -1387,11 +1522,6 @@ private async Task BeginCapture() return false; } - btnCapture.IsEnabled = false; - btnRepeat.IsEnabled = false; - btnOpenClose.IsEnabled = false; - btnAbort.IsEnabled = true; - mnuSettings.IsEnabled = false; return true; } @@ -1433,12 +1563,6 @@ private void tkInScreen_ValueChanged(object? sender, Avalonia.AvaloniaPropertyCh } - private void btnJmpTrigger_Click(object? sender, RoutedEventArgs e) - { - if (session?.CaptureChannels != null && session != null) - updateSamplesInDisplay((int)Math.Max(session.PreTriggerSamples - (tkInScreen.Value / 10), 0), (int)tkInScreen.Value); - } - private async void mnuSave_Click(object? sender, RoutedEventArgs e) { try @@ -1452,8 +1576,8 @@ private async void mnuSave_Click(object? sender, RoutedEventArgs e) return; var sets = session.Clone(); - sets.PreTriggerSamples = sampleViewer.PreSamples; - sets.LoopCount = sampleViewer.Bursts?.Length ?? 0; + //sets.PreTriggerSamples = sampleViewer.PreSamples; + //sets.LoopCount = sampleViewer.Bursts?.Length ?? 0; ExportedCapture ex = new ExportedCapture { Settings = sets, SelectedRegions = sampleViewer.Regions }; @@ -1489,6 +1613,9 @@ private async void mnuOpen_Click(object? sender, RoutedEventArgs e) if (ex == null) return; + string fileName = Path.GetFileName(file); + this.Title = Version + " - " + fileName; + session = ex.Settings; if (ex.Samples != null) @@ -1502,6 +1629,11 @@ private async void mnuOpen_Click(object? sender, RoutedEventArgs e) mnuSave.IsEnabled = true; mnuExport.IsEnabled = true; + if (driver != null) + { + btnOpenClose_Click(this, EventArgs.Empty); + } + driver = new EmulatedAnalyzerDriver(5); clearRegions(); @@ -1511,7 +1643,7 @@ private async void mnuOpen_Click(object? sender, RoutedEventArgs e) scrSamplePos.Maximum = session.TotalSamples - 1; - updateSamplesInDisplay(Math.Max(session.PreTriggerSamples - 10, 0), Math.Min(100, session.TotalSamples / 10)); + updateSamplesInDisplay(Math.Max(session.PreTriggerSamples - 10, 0), (int)tkInScreen.Value); LoadInfo(); } @@ -1602,14 +1734,14 @@ private void updateSampleDisplays() display.UpdateVisibleSamples((int)scrSamplePos.Value, (int)tkInScreen.Value); } - private void updateChannels() + private void updateChannels(bool ignoreSamples = false) { sampleViewer.BeginUpdate(); sampleViewer.PreSamples = session.PreTriggerSamples; sampleViewer.SetChannels(session.CaptureChannels, session.Frequency); sampleViewer.EndUpdate(); - samplePreviewer.UpdateSamples(session.CaptureChannels, session.TotalSamples); + samplePreviewer.UpdateSamples(session.CaptureChannels, ignoreSamples ? 0 : session.TotalSamples); samplePreviewer.ViewPosition = sampleViewer.FirstSample; channelViewer.Channels = session.CaptureChannels; @@ -1617,6 +1749,40 @@ private void updateChannels() sgManager.SetChannels(session.Frequency, session.CaptureChannels); } + private void syncUI() + { + bool hasDriver = driver != null && driver is not EmulatedAnalyzerDriver; + bool isCapturing = hasDriver && driver!.IsCapturing; + bool canCapture = hasDriver && !isCapturing; + bool canConfigureWiFi = hasDriver && driver.DriverType == AnalyzerDriverType.Serial && (driver.DeviceVersion?.Contains("WIFI") ?? false); + bool hasCapture = session != null && session.CaptureChannels?.FirstOrDefault()?.Samples?.Length == session.TotalSamples; + + btnOpenClose.IsEnabled = !isCapturing; + btnRefresh.IsEnabled = !hasDriver; + btnCapture.IsEnabled = canCapture; + btnRepeat.IsEnabled = canCapture; + btnAbort.IsEnabled = isCapturing; + + + mnuProfiles.IsEnabled = hasDriver && !isCapturing; + mnuSettings.IsEnabled = canConfigureWiFi; + mnuSave.IsEnabled = hasCapture; + mnuExport.IsEnabled = hasCapture; + + lblBootloader.IsVisible = hasDriver && !isCapturing; + lblInfo.IsVisible = hasDriver; + ddPorts.IsEnabled = !hasDriver; + + + lblConnectedDevice.Text = driver?.DeviceVersion ?? "< None >"; + btnOpenClose.Content = hasDriver ? "Close device" : "Open device"; + + if (!hasDriver) + RefreshPorts(); + + GetPowerStatus(); + } + class PortItem { public string? Icon { get; set; } diff --git a/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokDecodingTree.cs b/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokDecodingTree.cs index a8e17039..3625a6c5 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokDecodingTree.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokDecodingTree.cs @@ -10,6 +10,16 @@ namespace LogicAnalyzer.SigrokDecoderBridge public class SigrokDecodingTree { public List Branches { get; } = new List(); + + public SerializableDecodingTree ToSerializable() + { + var serializable = new SerializableDecodingTree(); + foreach (var branch in Branches) + { + serializable.Branches.Add(branch.ToSerializable()); + } + return serializable; + } } public class SigrokDecodingBranch @@ -20,5 +30,37 @@ public class SigrokDecodingBranch public required SigrokSelectedChannel[] Channels { get; set; } public List Children { get; } = new List(); + public SerializableDecodingBranch ToSerializable() + { + var serializable = new SerializableDecodingBranch + { + Name = Name, + DecoderId = Decoder.Id, + Options = Options.ToArray(), + Channels = Channels.ToArray() + }; + + foreach (var child in Children) + { + serializable.Children.Add(child.ToSerializable()); + } + + return serializable; + } + + } + + public class SerializableDecodingTree + { + public List Branches { get; } = new List(); + } + + public class SerializableDecodingBranch + { + public required string Name { get; set; } + public required string DecoderId { get; set; } + public required SigrokOptionValue[] Options { get; set; } + public required SigrokSelectedChannel[] Channels { get; set; } + public List Children { get; } = new List(); } } diff --git a/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokProvider.cs b/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokProvider.cs index 952a3ca4..d4733f5e 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokProvider.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokProvider.cs @@ -16,8 +16,8 @@ namespace SigrokDecoderBridge { public class SigrokProvider : IDisposable { - private Dictionary>? currentInputs; //= new Dictionary>(); - private Dictionary>? currentOutputs; //= new Dictionary>(); + private Dictionary>? currentInputs; + private Dictionary>? currentOutputs; private SigrokDecoderBase[]? decoders; @@ -36,7 +36,7 @@ private SigrokDecoderBase[] GetDecoders() { List classTemplates = new List(); - var dirs = Directory.GetDirectories(SigrokPythonEngine.DecoderPath); + var dirs = Directory.GetDirectories(SigrokPythonEngine.DecoderPath); foreach (var dir in dirs) { @@ -55,7 +55,7 @@ private SigrokDecoderBase[] GetDecoders() var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location); - var references = new MetadataReference[] + var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Object).Assembly.Location), diff --git a/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokPythonEngine.cs b/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokPythonEngine.cs index 73543360..d5a39d3c 100644 --- a/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokPythonEngine.cs +++ b/Software/LogicAnalyzer/LogicAnalyzer/SigrokDecoderBridge/SigrokPythonEngine.cs @@ -47,8 +47,21 @@ public static bool EnsureInitialized() if (File.Exists(cfgFile)) { Log("Reading python path from config file..."); - pythonPath = File.ReadAllText(cfgFile); - Log($"Stablished path: {pythonPath}"); + + string[] lines = File.ReadAllLines(cfgFile); + + if (lines.Length > 0) + { + pythonPath = lines[0].Trim().Replace("\n", "").Replace("\r", ""); + Log($"Stablished path: {pythonPath}"); + } + else + { + Log("Config file is empty, aborting startup."); + return false; + } + + } else { diff --git a/Software/LogicAnalyzer/SharedDriver/AnalyzerChannel.cs b/Software/LogicAnalyzer/SharedDriver/AnalyzerChannel.cs index ebc624a1..665d70c3 100644 --- a/Software/LogicAnalyzer/SharedDriver/AnalyzerChannel.cs +++ b/Software/LogicAnalyzer/SharedDriver/AnalyzerChannel.cs @@ -18,7 +18,7 @@ public class AnalyzerChannel public override string ToString() { - return ChannelName ?? TextualChannelNumber; + return string.IsNullOrWhiteSpace(ChannelName) ? TextualChannelNumber : ChannelName; } public AnalyzerChannel Clone() diff --git a/Software/LogicAnalyzer/SharedDriver/AnalyzerDriverBase.cs b/Software/LogicAnalyzer/SharedDriver/AnalyzerDriverBase.cs index 239a08ac..d68fd1fa 100644 --- a/Software/LogicAnalyzer/SharedDriver/AnalyzerDriverBase.cs +++ b/Software/LogicAnalyzer/SharedDriver/AnalyzerDriverBase.cs @@ -162,13 +162,13 @@ protected struct CaptureRequest public byte trigger; public byte invertedOrCount; public UInt16 triggerValue; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] channels; public byte channelCount; public UInt32 frequency; public UInt32 preSamples; public UInt32 postSamples; - public byte loopCount; + public ushort loopCount; public byte measure; public byte captureMode; } diff --git a/Software/LogicAnalyzer/SharedDriver/DeviceDetector.cs b/Software/LogicAnalyzer/SharedDriver/DeviceDetector.cs index dad1a773..0da92ef8 100644 --- a/Software/LogicAnalyzer/SharedDriver/DeviceDetector.cs +++ b/Software/LogicAnalyzer/SharedDriver/DeviceDetector.cs @@ -102,7 +102,7 @@ private static DetectedDevice[] DetectWindows() try { //Find serial devices - var rkUsbSer = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\usbser\Enum", false); + using var rkUsbSer = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\usbser\Enum", false); if (rkUsbSer != null) { @@ -143,7 +143,7 @@ private static DetectedDevice[] DetectWindows() //Read parent info - var rkEnum = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Enum\USB\VID_{vid}&PID_{pid}", false); + using var rkEnum = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Enum\USB\VID_{vid}&PID_{pid}", false); if (rkEnum != null) { @@ -151,7 +151,7 @@ private static DetectedDevice[] DetectWindows() { string keyPath = $@"SYSTEM\CurrentControlSet\Enum\USB\VID_{vid}&PID_{pid}\{devSer}"; - var rkDevice = Registry.LocalMachine.OpenSubKey(keyPath, false); + using var rkDevice = Registry.LocalMachine.OpenSubKey(keyPath, false); if (rkDevice == null) continue; @@ -172,7 +172,7 @@ private static DetectedDevice[] DetectWindows() foreach (var device in devices) { - var rkSer = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Enum\{device.DevicePath}\Device Parameters", false); + using var rkSer = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Enum\{device.DevicePath}\Device Parameters", false); if (rkSer != null) { diff --git a/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs b/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs index 6238d871..8bb39366 100644 --- a/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs +++ b/Software/LogicAnalyzer/SharedDriver/LogicAnalyzerDriver.cs @@ -1,4 +1,8 @@ -using System.Diagnostics; +#region Debug output control +//#define DEBUG_MODE +#endregion + +using System.Diagnostics; using System.IO.Ports; using System.Net; using System.Net.Http; @@ -82,14 +86,29 @@ public LogicAnalyzerDriver(string ConnectionString) private void InitSerialPort(string SerialPort, int Bauds) { - sp = new SerialPort(SerialPort, Bauds); - sp.RtsEnable = true; - sp.DtrEnable = true; - sp.NewLine = "\n"; - sp.ReadBufferSize = 1024 * 1024; - sp.WriteBufferSize = 1024 * 1024; - - sp.Open(); +#if DEBUG_MODE + + StoreDebugLog($"Initializing device in serial mode."); +#endif + try + { + sp = new SerialPort(SerialPort, Bauds); + sp.RtsEnable = true; + sp.DtrEnable = true; + sp.NewLine = "\n"; + sp.ReadBufferSize = 1024 * 1024; + sp.WriteBufferSize = 1024 * 1024; + + sp.Open(); + } + catch (Exception ex) + { +#if DEBUG_MODE + + StoreDebugLog($"Error initializing serial port: {ex.Message} - {ex.StackTrace}."); +#endif + throw; + } baseStream = sp.BaseStream; readResponse = new StreamReader(baseStream); @@ -103,10 +122,18 @@ private void InitSerialPort(string SerialPort, int Bauds) baseStream.ReadTimeout = 10000; version = readResponse.ReadLine(); +#if DEBUG_MODE + + StoreDebugLog($"Device version: {version}"); +#endif + var devVersion = VersionValidator.GetVersion(version); if (!devVersion.IsValid) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device version {version}, minimum supported version: V{VersionValidator.MAJOR_VERSION}_{VersionValidator.MINOR_VERSION}"); +#endif Dispose(); throw new DeviceConnectionException($"Invalid device version {DeviceVersion}, minimum supported version: V{VersionValidator.MAJOR_VERSION}_{VersionValidator.MINOR_VERSION}"); } @@ -114,6 +141,9 @@ private void InitSerialPort(string SerialPort, int Bauds) var freq = readResponse.ReadLine(); if (!GetFrequency(freq)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device frequency response, received value: {freq}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid device frequency response."); } @@ -121,6 +151,9 @@ private void InitSerialPort(string SerialPort, int Bauds) var blast = readResponse.ReadLine(); if(!GetBlastFrequency(blast)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid blast frequency response, received value: {blast}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid blast frequency response."); } @@ -128,6 +161,9 @@ private void InitSerialPort(string SerialPort, int Bauds) var bufString = readResponse.ReadLine(); if (!GetBufferSize(bufString)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device buffer size response, received value: {bufString}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid device buffer size response."); } @@ -135,25 +171,49 @@ private void InitSerialPort(string SerialPort, int Bauds) var chanString = readResponse.ReadLine(); if (!GetChannelCount(chanString)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device channel count response, received value: {chanString}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid device channel count response."); } baseStream.ReadTimeout = Timeout.Infinite; + +#if DEBUG_MODE + StoreDebugLog($"Device initialized successfully. Received parameters: {freq}, {blast}, {bufString}, {chanString}"); +#endif } private void InitNetwork(string AddressPort) { +#if DEBUG_MODE + + StoreDebugLog($"Initializing device in wifi mode."); +#endif + var match = regAddressPort.Match(AddressPort); if (match == null || !match.Success) + { +#if DEBUG_MODE + + StoreDebugLog($"Specified address/port is invalid."); +#endif throw new ArgumentException("Specified address/port is invalid"); + } devAddr = match.Groups[1].Value; string port = match.Groups[2].Value; if (!ushort.TryParse(port, out devPort)) + { +#if DEBUG_MODE + + StoreDebugLog($"Specified address/port is invalid."); +#endif throw new ArgumentException("Specified address/port is invalid"); + } tcpClient = new TcpClient(); @@ -171,10 +231,18 @@ private void InitNetwork(string AddressPort) baseStream.ReadTimeout = 10000; version = readResponse.ReadLine(); +#if DEBUG_MODE + + StoreDebugLog($"Device version: {version}"); +#endif + var devVersion = VersionValidator.GetVersion(version); if (!devVersion.IsValid) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device version {version}, minimum supported version: V{VersionValidator.MAJOR_VERSION}_{VersionValidator.MINOR_VERSION}"); +#endif Dispose(); throw new DeviceConnectionException($"Invalid device version {DeviceVersion}, minimum supported version: V{VersionValidator.MAJOR_VERSION}_{VersionValidator.MINOR_VERSION}"); } @@ -182,6 +250,9 @@ private void InitNetwork(string AddressPort) var freq = readResponse.ReadLine(); if (!GetFrequency(freq)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device frequency response, received value: {freq}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid device frequency response."); } @@ -189,6 +260,9 @@ private void InitNetwork(string AddressPort) var blast = readResponse.ReadLine(); if (!GetBlastFrequency(blast)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid blast frequency response, received value: {blast}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid blast frequency response."); } @@ -196,6 +270,9 @@ private void InitNetwork(string AddressPort) var bufString = readResponse.ReadLine(); if (!GetBufferSize(bufString)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device buffer size response, received value: {bufString}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid device buffer size response."); } @@ -203,6 +280,9 @@ private void InitNetwork(string AddressPort) var chanString = readResponse.ReadLine(); if (!GetChannelCount(chanString)) { +#if DEBUG_MODE + StoreDebugLog($"Invalid device channel count response, received value: {chanString}."); +#endif Dispose(); throw new DeviceConnectionException("Invalid device channel count response."); } @@ -210,6 +290,9 @@ private void InitNetwork(string AddressPort) baseStream.ReadTimeout = Timeout.Infinite; isNetwork = true; +#if DEBUG_MODE + StoreDebugLog($"Device initialized successfully. Received parameters: {freq}, {blast}, {bufString}, {chanString}"); +#endif } private bool GetChannelCount(string? chanString) { @@ -272,18 +355,36 @@ private bool GetBlastFrequency(string? blast) public override CaptureError StartCapture(CaptureSession Session, Action? CaptureCompletedHandler = null) { +#if DEBUG_MODE + StoreDebugLog($"Starting capture."); +#endif + try { if (capturing || baseStream == null || readResponse == null) + { +#if DEBUG_MODE + StoreDebugLog($"Error starting capture (busy)"); +#endif return CaptureError.Busy; + } if (Session.CaptureChannels == null || Session.CaptureChannels.Length < 0) + { +#if DEBUG_MODE + StoreDebugLog($"Error starting capture (bad params)"); +#endif return CaptureError.BadParams; - - int requestedSamples = Session.PreTriggerSamples + (Session.PostTriggerSamples * ((byte)Session.LoopCount + 1)); + } + int requestedSamples = Session.PreTriggerSamples + (Session.PostTriggerSamples * ((ushort)Session.LoopCount + 1)); if (!ValidateSettings(Session, requestedSamples)) + { +#if DEBUG_MODE + StoreDebugLog($"Error starting capture (invalid settings)"); +#endif return CaptureError.BadParams; + } var mode = GetCaptureMode(Session.CaptureChannels.Select(c => c.ChannelNumber).ToArray()); @@ -304,11 +405,25 @@ public override CaptureError StartCapture(CaptureSession Session, Action ReadCapture(Session, requestedSamples, mode, CaptureCompletedHandler)); +#if DEBUG_MODE + StoreDebugLog($"Capture started successfully."); +#endif return CaptureError.None; } + +#if DEBUG_MODE + StoreDebugLog($"Error starting capture (bad device response, received response: {result})"); +#endif + return CaptureError.HardwareError; } - catch { return CaptureError.HardwareError; } + catch (Exception ex) + { +#if DEBUG_MODE + StoreDebugLog($"Unhandled exception starting capture: {ex.Message} - {ex.StackTrace}"); +#endif + return CaptureError.HardwareError; + } } private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, Action? CaptureCompletedHandler) @@ -316,7 +431,12 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, try { if (readData == null) + { +#if DEBUG_MODE + StoreDebugLog($"Error reading capture, no data reader available."); +#endif throw new Exception("No data reader available"); + } uint length = readData.ReadUInt32(); UInt32[] samples = new UInt32[length]; @@ -325,9 +445,17 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, BinaryReader rdData; if (isNetwork) + { +#if DEBUG_MODE + StoreDebugLog($"Reading network data"); +#endif rdData = readData; + } else { +#if DEBUG_MODE + StoreDebugLog($"Reading serial data"); +#endif int bufLen = Samples * (Mode == CaptureMode.Channels_8 ? 1 : (Mode == CaptureMode.Channels_16 ? 2 : 4)); if (Session.LoopCount == 0 || !Session.MeasureBursts) @@ -352,14 +480,23 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, switch (Mode) { case CaptureMode.Channels_8: +#if DEBUG_MODE + StoreDebugLog($"Reading 8 bit data"); +#endif for (int buc = 0; buc < length; buc++) samples[buc] = rdData.ReadByte(); break; case CaptureMode.Channels_16: +#if DEBUG_MODE + StoreDebugLog($"Reading 16 bit data"); +#endif for (int buc = 0; buc < length; buc++) samples[buc] = rdData.ReadUInt16(); break; case CaptureMode.Channels_24: +#if DEBUG_MODE + StoreDebugLog($"Reading 24 bit data"); +#endif for (int buc = 0; buc < length; buc++) samples[buc] = rdData.ReadUInt32(); break; @@ -369,6 +506,9 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, if (stampLength > 0) { +#if DEBUG_MODE + StoreDebugLog($"Timestamp data included in capture"); +#endif for (int buc = 0; buc < Session.LoopCount + 2; buc++) timestamps[buc] = rdData.ReadUInt32(); } @@ -379,6 +519,12 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, //by the device, so we need to adjust the timestamps to be more accurate if (timestamps.Length > 0) { +#if DEBUG_MODE + StoreDebugLog($"Reading timestamp data"); +#endif + + double tickLength = 1000000000.0 / blastFrequency; + //First we invert the lower part of the timestamps as systick counts in decreasing order for (int buc = 0; buc < timestamps.Length; buc++) { @@ -390,12 +536,12 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, //Next we calculate the ns per sample and the ns per burst double nsPerSample = 1000000000.0 / Session.Frequency; - double ticksPerSample = nsPerSample / 5; + double ticksPerSample = nsPerSample / tickLength; double nsPerBurst = nsPerSample * Session.PostTriggerSamples; //We calculate the ticks per burst, as we know the device's CPU runs at 200Mhz we know that each //tick is 5ns, so we can determine how many ticks happen per burst - double ticksPerBurst = nsPerBurst / 5; + double ticksPerBurst = nsPerBurst / tickLength; for (int buc = 1; buc < timestamps.Length; buc++) { @@ -406,7 +552,6 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, //If the difference between the timestamps is less than the ticks per burst, we adjust the timestamps if (top - timestamps[buc - 1] <= ticksPerBurst) { - Debug.WriteLine($"Adjusting timestamp {buc}"); uint diff = (uint)(ticksPerBurst - (top - timestamps[buc - 1]) + (ticksPerSample * 2)); for (int buc2 = buc; buc2 < timestamps.Length; buc2++) @@ -423,7 +568,7 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, { //In case of rollback, we need to adjust the timestamps ulong top = timestamps[buc] < timestamps[buc - 1] ? timestamps[buc] + 0xFFFFFFFF : timestamps[buc]; - delays[buc - 2] = (UInt64)((top - timestamps[buc - 1]) - ticksPerBurst) * 5; + delays[buc - 2] = (UInt64)(((top - timestamps[buc - 1]) - ticksPerBurst) * tickLength); Debug.WriteLine(delays[buc - 2]); } @@ -457,12 +602,19 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, //timestamps = delays; } - Session.Bursts = bursts.ToArray(); +#if DEBUG_MODE + StoreDebugLog($"Extracting samples..."); +#endif + for (int buc = 0; buc < Session.CaptureChannels.Length; buc++) ExtractSamples(Session.CaptureChannels[buc], buc, samples); +#if DEBUG_MODE + StoreDebugLog($"Capture read complete"); +#endif + if (CaptureCompletedHandler != null) CaptureCompletedHandler(new CaptureEventArgs { Success = true, Session = Session }); else if (CaptureCompleted != null) @@ -491,6 +643,12 @@ private void ReadCapture(CaptureSession Session, int Samples, CaptureMode Mode, if (!capturing) return; +#if DEBUG_MODE + StoreDebugLog($"Unhandled exception reading capture: {ex.Message} - {ex.StackTrace}"); +#endif + + capturing = false; + if (CaptureCompletedHandler != null) CaptureCompletedHandler(new CaptureEventArgs { Success = false, Session = Session }); else if (CaptureCompleted != null) @@ -522,7 +680,7 @@ private CaptureRequest ComposeRequest(CaptureSession session, int requestedSampl frequency = (uint)session.Frequency, preSamples = (uint)session.PreTriggerSamples, postSamples = (uint)session.PostTriggerSamples, - loopCount = (byte)session.LoopCount, + loopCount = (ushort)session.LoopCount, measure = session.MeasureBursts ? (byte)1 : (byte)0, captureMode = (byte)mode }; @@ -579,7 +737,9 @@ private bool ValidateSettings(CaptureSession session, int requestedSamples) requestedSamples > captureLimits.MaxTotalSamples || session.Frequency < MinFrequency || session.Frequency > MaxFrequency || - session.LoopCount > 254 + (session.MeasureBursts && session.LoopCount > 254) || + (session.MeasureBursts && session.PostTriggerSamples < 100) || + session.LoopCount > 65534 ) return false; } @@ -693,33 +853,38 @@ public override bool EnterBootloader() #region Network-related functions public override unsafe bool SendNetworkConfig(string AccesPointName, string Password, string IPAddress, ushort Port) { - if (isNetwork || baseStream == null || readResponse == null) - return false; + try + { + if (isNetwork || baseStream == null || readResponse == null) + return false; - NetConfig request = new NetConfig { Port = Port }; - byte[] name = Encoding.ASCII.GetBytes(AccesPointName); - byte[] pass = Encoding.ASCII.GetBytes(Password); - byte[] addr = Encoding.ASCII.GetBytes(IPAddress); + NetConfig request = new NetConfig { Port = Port }; + byte[] name = Encoding.ASCII.GetBytes(AccesPointName); + byte[] pass = Encoding.ASCII.GetBytes(Password); + byte[] addr = Encoding.ASCII.GetBytes(IPAddress); - Marshal.Copy(name, 0, new IntPtr(request.AccessPointName), name.Length); - Marshal.Copy(pass, 0, new IntPtr(request.Password), pass.Length); - Marshal.Copy(addr, 0, new IntPtr(request.IPAddress), addr.Length); + Marshal.Copy(name, 0, new IntPtr(request.AccessPointName), name.Length); + Marshal.Copy(pass, 0, new IntPtr(request.Password), pass.Length); + Marshal.Copy(addr, 0, new IntPtr(request.IPAddress), addr.Length); - OutputPacket pack = new OutputPacket(); - pack.AddByte(2); - pack.AddStruct(request); + OutputPacket pack = new OutputPacket(); + pack.AddByte(2); + pack.AddStruct(request); - baseStream.Write(pack.Serialize()); - baseStream.Flush(); + baseStream.Write(pack.Serialize()); + baseStream.Flush(); - baseStream.ReadTimeout = 5000; - var result = readResponse.ReadLine(); - baseStream.ReadTimeout = Timeout.Infinite; + baseStream.ReadTimeout = 5000; + var result = readResponse.ReadLine(); + baseStream.ReadTimeout = Timeout.Infinite; - if (result == "SETTINGS_SAVED") - return true; + if (result == "SETTINGS_SAVED") + return true; - return false; + return false; + } + catch + { return false; } } public override string? GetVoltageStatus() { @@ -780,6 +945,10 @@ public bool StopBlink() #region IDisposable implementation public override void Dispose() { + + if (IsCapturing) + StopCapture(); + try { sp.Close(); @@ -824,5 +993,18 @@ public override void Dispose() CaptureCompleted = null; } #endregion + + #region Debug functions +#if DEBUG_MODE + private void StoreDebugLog(string Message, [CallerMemberName] string Caller = "") + { + try + { + File.AppendAllLines("driver_debug.log", new string[] { $"[{Caller}] {Message}" }); + } + catch { } + } +#endif + #endregion } } \ No newline at end of file diff --git a/Software/LogicAnalyzer/SharedDriver/VersionValidator.cs b/Software/LogicAnalyzer/SharedDriver/VersionValidator.cs index af6f04be..d6155389 100644 --- a/Software/LogicAnalyzer/SharedDriver/VersionValidator.cs +++ b/Software/LogicAnalyzer/SharedDriver/VersionValidator.cs @@ -10,7 +10,7 @@ namespace SharedDriver internal static class VersionValidator { public const int MAJOR_VERSION = 6; - public const int MINOR_VERSION = 0; + public const int MINOR_VERSION = 5; static Regex regVersion = new Regex(".*?(V([0-9]+)_([0-9]+))$"); diff --git a/Software/decoders/arm_etmv3/pd.py b/Software/decoders/arm_etmv3/pd.py index a0bbd94c..95a7eb7d 100644 --- a/Software/decoders/arm_etmv3/pd.py +++ b/Software/decoders/arm_etmv3/pd.py @@ -212,10 +212,10 @@ def load_objdump(self): disasm = disasm.decode('utf-8', 'replace') - instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*') - branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)') - filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?') - funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*') + instpat = re.compile(r'\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*') + branchpat = re.compile(r'(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)') + filepat = re.compile(r'[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?') + funcpat = re.compile(r'[0-9a-fA-F]+\s*<([^>]+)>:.*') prev_src = '' prev_file = '' diff --git a/Software/decoders/arm_itm/pd.py b/Software/decoders/arm_itm/pd.py index 8ceeac46..136ef76f 100644 --- a/Software/decoders/arm_itm/pd.py +++ b/Software/decoders/arm_itm/pd.py @@ -113,9 +113,9 @@ def load_objdump(self): disasm = disasm.decode('utf-8', 'replace') - instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*') - filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?') - funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*') + instpat = re.compile(r'\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*') + filepat = re.compile(r'[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?') + funcpat = re.compile(r'[0-9a-fA-F]+\s*<([^>]+)>:.*') prev_file = '' prev_func = '' diff --git a/Software/decoders/avr_isp/parts.py b/Software/decoders/avr_isp/parts.py index fee4d9b0..5a676af9 100644 --- a/Software/decoders/avr_isp/parts.py +++ b/Software/decoders/avr_isp/parts.py @@ -31,7 +31,7 @@ (0x90, 0x01): 'AT90S1200', (0x90, 0x05): 'ATtiny12', (0x90, 0x06): 'ATtiny15', - (0x90, 0x07): 'ATtiny13', + (0x90, 0x07): 'ATtiny13(A)', (0x91, 0x01): 'AT90S2313', (0x91, 0x02): 'AT90S2323', (0x91, 0x03): 'AT90S2343', @@ -40,58 +40,124 @@ (0x91, 0x07): 'ATtiny28', (0x91, 0x08): 'ATtiny25', (0x91, 0x09): 'ATtiny26', - (0x91, 0x0A): 'ATtiny2313', - (0x91, 0x0B): 'ATtiny24', - (0x91, 0x0C): 'ATtiny261', + (0x91, 0x0A): 'ATtiny2313(A)', + (0x91, 0x0B): 'ATtiny24(A)', + (0x91, 0x0C): 'ATtiny261(A)', (0x92, 0x01): 'AT90S4414', + (0x92, 0x02): 'AT90S4434', (0x92, 0x03): 'AT90S4433', (0x92, 0x05): 'ATmega48(A)', (0x92, 0x06): 'ATtiny45', - (0x92, 0x08): 'ATtiny461', + (0x92, 0x07): 'ATtiny44(A)', + (0x92, 0x08): 'ATtiny461(A)', (0x92, 0x09): 'ATtiny48', - (0x92, 0x0A): 'ATmega48PA', + (0x92, 0x0A): 'ATmega48P(A)', + (0x92, 0x0C): 'ATtiny43U', (0x92, 0x0D): 'ATtiny4313', (0x92, 0x10): 'ATmega48PB', + (0x92, 0x15): 'ATtiny441', (0x93, 0x01): 'AT90S8515', (0x93, 0x03): 'AT90S8535', - (0x93, 0x07): 'ATmega8', + (0x93, 0x06): 'ATmega8515', + (0x93, 0x07): 'ATmega8(A)', + (0x93, 0x08): 'ATmega8535', (0x93, 0x0A): 'ATmega88(A)', (0x93, 0x0B): 'ATtiny85', - (0x93, 0x0D): 'ATtiny861', - (0x93, 0x0F): 'ATmega88PA', + (0x93, 0x0C): 'ATtiny84(A)', + (0x93, 0x0D): 'ATtiny861(A)', + (0x93, 0x0F): 'ATmega88P(A)', + (0x93, 0x10): 'ATmega8HVA', (0x93, 0x11): 'ATtiny88', + (0x93, 0x14): 'ATtiny828(R)', + (0x93, 0x15): 'ATtiny841', (0x93, 0x16): 'ATmega88PB', + (0x93, 0x81): 'AT90PWM(2)(3)', + (0x93, 0x82): 'AT90USB82', + (0x93, 0x83): 'AT90PWM(1)(2B)(3B)', + (0x93, 0x87): 'ATtiny87', + (0x93, 0x88): 'AT90PWM81', (0x93, 0x89): 'ATmega8U2', (0x94, 0x01): 'ATmega161', (0x94, 0x02): 'ATmega163', - (0x94, 0x03): 'ATmega16', + (0x94, 0x03): 'ATmega16(A)', (0x94, 0x04): 'ATmega162', + (0x94, 0x05): 'ATmega169(P)(PA)', (0x94, 0x06): 'ATmega168(A)', - (0x94, 0x0A): 'ATmega164PA', - (0x94, 0x0B): 'ATmega168PA', + (0x94, 0x07): 'ATmega165(P)(PA)', + (0x94, 0x0A): 'ATmega164P(A)', + (0x94, 0x0B): 'ATmega168P(A)', + (0x94, 0x0C): 'ATmega16HVA', + (0x94, 0x0D): 'ATmega16HVB(rev. B)', (0x94, 0x0F): 'ATmega164A', - (0x94, 0x12): 'ATtiny1634', + (0x94, 0x10): 'ATmega165A', + (0x94, 0x11): 'ATmega169A', + (0x94, 0x12): 'ATtiny1634(R)', (0x94, 0x15): 'ATmega168PB', + (0x94, 0x82): 'AT90USB162', + (0x94, 0x83): 'AT90PWM(216)(316)', + (0x94, 0x84): 'ATmega16M1', + (0x94, 0x87): 'ATtiny167', (0x94, 0x88): 'ATmega16U4', (0x94, 0x89): 'ATmega16U2', - (0x95, 0x01): 'ATmega32', + (0x94, 0x8B): 'AT90PWM161', (0x95, 0x01): 'ATmega323', + (0x95, 0x02): 'ATmega32(A)', + (0x95, 0x03): 'ATmega329(A)', + (0x95, 0x04): 'ATmega3290(A)', + (0x95, 0x05): 'ATmega325(A)', + (0x95, 0x06): 'ATmega3250(A)', + (0x95, 0x08): 'ATmega324P', + (0x95, 0x0B): 'ATmega329P(A)', + (0x95, 0x0C): 'ATmega3290P(A)', + (0x95, 0x0D): 'ATmega325P(A)', + (0x95, 0x0E): 'ATmega3250P(A)', (0x95, 0x0F): 'ATmega328P', + (0x95, 0x10): 'ATmega32HVB(rev. B)', (0x95, 0x11): 'ATmega324PA', + (0x95, 0x13): 'ATmega32HVE2', (0x95, 0x14): 'ATmega328', (0x95, 0x15): 'ATmega324A', + (0x95, 0x16): 'ATmega328PB', + (0x95, 0x17): 'ATmega324PB', + (0x95, 0x81): 'AT90CAN32', + (0x95, 0x84): 'ATmega32M1', + (0x95, 0x86): 'ATmega32C1', (0x95, 0x87): 'ATmega32U4', (0x95, 0x8A): 'ATmega32U2', + (0x96, 0x02): 'ATmega64(A)', + (0x96, 0x03): 'ATmega649(A)', + (0x96, 0x04): 'ATmega6490', + (0x96, 0x05): 'ATmega645(A)', + (0x96, 0x06): 'ATmega6450(A)', (0x96, 0x08): 'ATmega640', (0x96, 0x09): 'ATmega644(A)', - (0x96, 0x0A): 'ATmega644PA', + (0x96, 0x0A): 'ATmega644P(A)', + (0x96, 0x0B): 'ATmega649P', + (0x96, 0x0C): 'ATmega6490P', + (0x96, 0x0D): 'ATmega645P', + (0x96, 0x0E): 'ATmega6450P', + (0x96, 0x10): 'ATmega64HVE2', + (0x96, 0x81): 'AT90CAN64', + (0x96, 0x82): 'AT90USB(646)(647)', + (0x96, 0x84): 'ATmega64M1', + (0x96, 0x86): 'ATmega64C1', (0x97, 0x01): 'ATmega103', + (0x97, 0x02): 'ATmega128(A)', (0x97, 0x03): 'ATmega1280', (0x97, 0x04): 'ATmega1281', (0x97, 0x05): 'ATmega1284P', (0x97, 0x06): 'ATmega1284', + (0x97, 0x81): 'AT90CAN128', + (0x97, 0x82): 'AT90USB(1286)(1287)', (0x98, 0x01): 'ATmega2560', (0x98, 0x02): 'ATmega2561', + (0xA6, 0x02): 'ATmega64RFR2', + (0xA6, 0x03): 'ATmega644RFR2', + (0xA7, 0x01): 'ATmega128RFA1', + (0xA7, 0x02): 'ATmega128RFR2', + (0xA7, 0x03): 'ATmega1284RFR2', + (0xA8, 0x02): 'ATmega256RFR2', + (0xA8, 0x03): 'ATmega2564RFR2', (0xFF, 0xFF): 'Device code erased, or target missing', (0x01, 0x02): 'Device locked', } diff --git a/Software/decoders/counter/pd.py b/Software/decoders/counter/pd.py index f1134bd1..bc26d940 100644 --- a/Software/decoders/counter/pd.py +++ b/Software/decoders/counter/pd.py @@ -53,7 +53,7 @@ class Decoder(srd.Decoder): 'values': ('any', 'rising', 'falling')}, {'id': 'divider', 'desc': 'Count divider (word width)', 'default': 0}, {'id': 'reset_edge', 'desc': 'Edge which clears counters (reset)', - 'default': 'falling', 'values': ('rising', 'falling')}, + 'default': 'falling', 'values': ('any', 'rising', 'falling')}, {'id': 'edge_off', 'desc': 'Edge counter value after start/reset', 'default': 0}, {'id': 'word_off', 'desc': 'Word counter value after start/reset', 'default': 0}, {'id': 'dead_cycles', 'desc': 'Ignore this many edges after reset', 'default': 0}, diff --git a/Software/decoders/ds1307/pd.py b/Software/decoders/ds1307/pd.py index 51d673d3..f93ae63e 100644 --- a/Software/decoders/ds1307/pd.py +++ b/Software/decoders/ds1307/pd.py @@ -48,11 +48,11 @@ def regs_and_bits(): l = [('reg_' + r.lower(), r + ' register') for r in regs] - l += [('bit_' + re.sub('\/| ', '_', b).lower(), b + ' bit') for b in bits] + l += [('bit_' + re.sub(r'\/| ', '_', b).lower(), b + ' bit') for b in bits] return tuple(l) a = ['REG_' + r.upper() for r in regs] + \ - ['BIT_' + re.sub('\/| ', '_', b).upper() for b in bits] + \ + ['BIT_' + re.sub(r'\/| ', '_', b).upper() for b in bits] + \ ['READ_DATE_TIME', 'WRITE_DATE_TIME', 'READ_REG', 'WRITE_REG', 'WARNING'] Ann = SrdIntEnum.from_list('Ann', a) diff --git a/Software/decoders/edid/config b/Software/decoders/edid/config new file mode 100644 index 00000000..44e1f353 --- /dev/null +++ b/Software/decoders/edid/config @@ -0,0 +1 @@ +extra-install pnpids.txt diff --git a/Software/decoders/max7219/__init__.py b/Software/decoders/max72xx/__init__.py similarity index 94% rename from Software/decoders/max7219/__init__.py rename to Software/decoders/max72xx/__init__.py index 673d3040..69bd6199 100644 --- a/Software/decoders/max7219/__init__.py +++ b/Software/decoders/max72xx/__init__.py @@ -18,8 +18,8 @@ ## ''' -This decoder stacks on top of the 'spi' PD and decodes the Maxim MAX7219 and -MAX7221 LED matrix driver protocol. +This decoder stacks on top of the 'spi' PD and decodes the Maxim MAX7219 +and MAX7221 LED matrix driver protocol. ''' from .pd import Decoder diff --git a/Software/decoders/max7219/pd.py b/Software/decoders/max72xx/pd.py similarity index 56% rename from Software/decoders/max7219/pd.py rename to Software/decoders/max72xx/pd.py index eb26e105..efa85938 100644 --- a/Software/decoders/max7219/pd.py +++ b/Software/decoders/max72xx/pd.py @@ -38,24 +38,29 @@ def _decode_intensity(val): 0x0F: ['Display test', lambda v: 'on' if v else 'off'] } -ann_reg, ann_digit, ann_warning = range(3) +ann_chip, ann_reg, ann_digit, ann_warning = range(4) class Decoder(srd.Decoder): api_version = 3 - id = 'max7219' - name = 'MAX7219' + id = 'max72xx' + name = 'MAX72xx' longname = 'Maxim MAX7219/MAX7221' - desc = 'Maxim MAX72xx series 8-digit LED display driver.' + desc = 'Maxim MAX72xx series 8-digit LED display driver' license = 'gplv2+' inputs = ['spi'] outputs = [] tags = ['Display'] + options = ( + {'id': 'numofdrivers', 'desc': 'Number of daisy-chained chips', 'default': 1}, + ) annotations = ( - ('register', 'Register write'), - ('digit', 'Digit displayed'), - ('warning', 'Warning'), + ('chip', 'Index of chip in daisy chain'), + ('register', 'Registers written to the device'), + ('digit', 'Digits displayed on the device'), + ('warning', 'Human-readable warnings'), ) annotation_rows = ( + ('chip_nr', 'Chip number', (ann_chip,)), ('commands', 'Commands', (ann_reg, ann_digit)), ('warnings', 'Warnings', (ann_warning,)), ) @@ -70,9 +75,16 @@ def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.pos = 0 self.cs_start = 0 + self.num_of_drivers = self.options['numofdrivers'] + + def putchip(self, ss, es, chip): + self.put(ss, es, self.out_ann, [ann_chip, ['Chip %d' % (chip)]]) def putreg(self, ss, es, reg, value): - self.put(ss, es, self.out_ann, [ann_reg, ['%s: %s' % (reg, value)]]) + if value: + self.put(ss, es, self.out_ann, [ann_reg, ['%s: %s' % (reg, value)]]) + else: + self.put(ss, es, self.out_ann, [ann_reg, [reg]]) def putdigit(self, ss, es, digit, value): self.put(ss, es, self.out_ann, [ann_digit, ['Digit %d: %02X' % (digit, value)]]) @@ -87,18 +99,21 @@ def decode(self, ss, es, data): if not self.cs_asserted: return - if self.pos == 0: - self.addr = mosi - self.addr_start = ss - elif self.pos == 1: - if self.addr >= 1 and self.addr <= 8: - self.putdigit(self.addr_start, es, self.addr, mosi) - elif self.addr in registers: - name, decoder = registers[self.addr] - self.putreg(self.addr_start, es, name, decoder(mosi)) - else: - self.putwarn(self.addr_start, es, - 'Unknown register %02X' % (self.addr)) + if self.pos <= self.num_of_drivers * 2: + if self.pos % 2 == 0: + self.addr = mosi + self.addr_start = ss + elif self.pos % 2 == 1: + if self.num_of_drivers > 1: + self.putchip(self.addr_start, es, (self.pos // 2) + 1) + if self.addr >= 1 and self.addr <= 8: + self.putdigit(self.addr_start, es, self.addr, mosi) + elif self.addr in registers: + name, decoder = registers[self.addr] + self.putreg(self.addr_start, es, name, decoder(mosi)) + else: + self.putwarn(self.addr_start, es, + 'Unknown register %02X' % (self.addr)) self.pos += 1 elif ptype == 'CS-CHANGE': @@ -107,9 +122,9 @@ def decode(self, ss, es, data): self.pos = 0 self.cs_start = ss else: - if self.pos == 1: + if self.pos > 0 and self.pos < 2 * self.num_of_drivers: # Don't warn if pos=0 so that CS# glitches don't appear # as spurious warnings. - self.putwarn(self.cs_start, es, 'Short write') - elif self.pos > 2: - self.putwarn(self.cs_start, es, 'Overlong write') + self.putwarn(self.cs_start, es, 'Not enough bits sent for current number of chips') + elif self.pos > 2 * self.num_of_drivers: + self.putwarn(self.cs_start, es, 'Too many bits sent for current number of chips') diff --git a/Software/decoders/smartcard/__init__.py b/Software/decoders/mcp230xx/__init__.py similarity index 79% rename from Software/decoders/smartcard/__init__.py rename to Software/decoders/mcp230xx/__init__.py index 5afc9f39..cee4c79e 100644 --- a/Software/decoders/smartcard/__init__.py +++ b/Software/decoders/mcp230xx/__init__.py @@ -1,7 +1,7 @@ ## ## This file is part of the libsigrokdecode project. ## -## Copyright (C) 2012 Uwe Hermann +## Copyright (C) 2019 Benedikt Otto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -18,8 +18,8 @@ ## ''' -ISO7816 is a bidirectional, multi-master -bus using two signals (CLK = serial clock line, DATA = IO data line). +This decoder stacks on top of the 'i2c' PD and decodes the Microchip +8-bit MCP23008 and 16-bit MCP23017 I²C output expander protocol. ''' from .pd import Decoder diff --git a/Software/decoders/mcp230xx/pd.py b/Software/decoders/mcp230xx/pd.py new file mode 100644 index 00000000..58a1bce6 --- /dev/null +++ b/Software/decoders/mcp230xx/pd.py @@ -0,0 +1,143 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2019 Benedikt Otto +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd + +STATE_IDLE, STATE_ADDR, STATE_DATA, STATE_READ_ADDR, STATE_READ_DATA, STATE_STOP = range(6) +UNKNOWN, READ, WRITE = range(3) + +registers = ["IODIR", "IPOL", "GPINTEN", "DEFVAL", "INTCON", "IOCON", "GPPU", "INTF", "INTCAP", "GPIO", "OLAT"] +registers_mcp23017_bank0 = {i: (registers[i // 2] + "AB"[i % 2] if registers[i // 2] != "IOCON" else "IOCON") for i in range(22)} + +registers_mcp23017_bank1 = {(i + 5 if i > 11 else i): (registers[i % 11] + "AB"[i // 11] if registers[i % 11] != "IOCON" else "IOCON") for i in range(22)} + +registers_mcp23008 = {i: registers[i] for i in range(11)} + +class Decoder(srd.Decoder): + api_version = 3 + id = 'mcp230xx' + name = 'MCP230XX' + longname = 'Microchip MCP230XX' + desc = 'MCP230XX 8/16-bit I²C output expanders.' + license = 'gplv2+' + inputs = ['i2c'] + outputs = [] + tags = ['IC'] + + options = ( + {'id': 'type', 'desc': 'Type', 'default': 'MCP23017', + 'values': ('MCP23008', 'MCP23017')}, + ) + + annotations = ( + ('register_read', 'Register read'), + ('register_write', 'Register write'), + ('warning', 'Warning'), + ) + annotation_rows = ( + ('regs', 'Registers', (0, 1)), + ('warnings', 'Warnings', (2,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.state = STATE_IDLE + self.iocon = 0 + self.iocon_set = False + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def get_registers(self): + if self.options["type"] == "MCP23008": + return registers_mcp23008 + else: + return registers_mcp23017_bank1 if self.iocon & (1 << 7) else registers_mcp23017_bank0 + + def putx(self, ss, es, data): + self.put(ss, es, self.out_ann, data) + + def checkAddress(self, ss, es, address): + if not address in range(0x20, 0x27 + 1): + self.putx(ss, es, [2, ['Address %02X not MCP230XX compatible' % address]]) + + def handleRead(self, register, data): + if len(data) >= 1: + register = register[0] + for d in data: + registers = self.get_registers() + if not register in registers: + self.putx(d[1], d[2], [2, ['Error: Register %d not accessible' %register]]) + if not self.iocon_set: + self.iocon = d[0] + else: + register_name = registers[register] + if register_name == "IOCON": + self.iocon = d[0] + self.iocon_set = True + self.putx(d[1], d[2], [0, ["Read %s: %02X" % (register_name, d[0]), "R%02X" % d[0]]]) + register += 1 + + def handleWrite(self, data): + if len(data) >= 2: + register = data[0][0] + for d in data[1:]: + registers = self.get_registers() + if not register in registers: + self.putx(d[1], d[2], [2, ['Error: Register %d not accessible' %register]]) + if not self.iocon_set: + self.iocon = d[0] + else: + register_name = registers[register] + if register_name == "IOCON": + self.iocon = d[0] + self.iocon_set = True + self.putx(d[1], d[2], [1, ["Write %s: %02X" % (register_name, d[0]), "W%02X" % d[0]]]) + register += 1 + + def decode(self, ss, es, data): + cmd, databyte = data + if cmd in ('ACK', 'NACK', 'BITS'): # Discard 'ACK' and 'BITS'. + return + if self.state == STATE_IDLE and cmd == 'START': + self.state = STATE_ADDR + self.dataWrite = [] + self.dataRead = [] + elif self.state == STATE_ADDR and cmd == 'ADDRESS WRITE': + self.state = STATE_DATA + self.checkAddress(ss, es, databyte) + elif self.state in [STATE_DATA, STATE_STOP] and cmd == 'DATA WRITE': + self.state = STATE_STOP + self.dataWrite.append((databyte, ss, es)) + elif self.state == STATE_STOP and cmd == "START REPEAT": + self.state = STATE_READ_ADDR + elif self.state == STATE_READ_ADDR and cmd == "ADDRESS READ": + self.state = STATE_READ_DATA + self.checkAddress(ss, es, databyte) + elif self.state in [STATE_READ_DATA, STATE_STOP] and cmd == 'DATA READ': + self.state = STATE_STOP + self.dataRead.append((databyte, ss, es)) + elif self.state == STATE_STOP and cmd == 'STOP': + self.state = STATE_IDLE + if len(self.dataRead) > 0 and len(self.dataWrite) == 1: + self.handleRead(self.dataWrite[0], self.dataRead) + elif len(self.dataWrite) > 0 and self.dataRead == []: + self.handleWrite(self.dataWrite) diff --git a/Software/decoders/mos6502/__init__.py b/Software/decoders/mos6502/__init__.py new file mode 100644 index 00000000..bd882fe3 --- /dev/null +++ b/Software/decoders/mos6502/__init__.py @@ -0,0 +1,29 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 David Banks +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' +The 6502 is an 8-bit microprocessor. + +In addition to the 8-bit data bus, this decoder requires the input signals +SYNC (sync) and RnW (read / not write) to do its work. An explicit +clock signal is not required. However, the 6502 CPU clock may be used as +sampling clock, if applicable. +''' + +from .pd import Decoder diff --git a/Software/decoders/mos6502/pd.py b/Software/decoders/mos6502/pd.py new file mode 100644 index 00000000..b754a5d7 --- /dev/null +++ b/Software/decoders/mos6502/pd.py @@ -0,0 +1,267 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 David Banks +## Update 2025 by Emile : most of David's code rewritten. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd +from functools import reduce +from .tables import addr_mode_len_map, instr_table, AddrMode +import string + +class Ann: + DATA, FETCH, OP1, OP2, MEMRD, MEMWR, INSTR, ADDR = range(8) + +class Pin: + D0, D7 = 0, 7 + A0, A15 = 8, 23 + RNW, SYNC, PHI2, RDYN, IRQN, NMIN, RSTN = range(24, 31) + +class Cycle: + FETCH, OP1, OP2, MEMRD, MEMWR, DATA = range(6) + +cycle_to_ann_map = { + Cycle.FETCH: Ann.FETCH, # Fetch opcode + Cycle.OP1: Ann.OP1, # Read 1st databyte + Cycle.OP2: Ann.OP2, # Read 2nd databyte (MSB) + Cycle.MEMRD: Ann.MEMRD, # Read byte + Cycle.MEMWR: Ann.MEMWR, # Write byte + Cycle.DATA: Ann.DATA, # Dummy cycle +} + +cycle_to_name_map = { + Cycle.FETCH: 'Fetch', + Cycle.OP1: 'Op1', + Cycle.OP2: 'Op2', + Cycle.MEMRD: 'Read', + Cycle.MEMWR: 'Write', + Cycle.DATA: 'Data', +} + +def reduce_bus(bus): + if 0xFF in bus: + return None # unassigned bus channels + else: + return reduce(lambda a, b: (a << 1) | b, reversed(bus)) + +def signed_byte(byte): + return byte if byte < 128 else byte - 256 + +class Decoder(srd.Decoder): + api_version = 3 + id = 'mos6502' + name = 'MOS6502' + longname = 'Mostek 6502 CPU' + desc = 'Mostek 6502 microprocessor disassembly.' + license = 'gplv3+' + inputs = ['logic'] + outputs = [] + tags = ['Retro computing'] + channels = tuple({ + 'id': 'd%d' % i, + 'name': 'D%d' % i, + 'desc': 'Data bus line %d' % i + } for i in range(8) + ) + tuple({ + 'id': 'a%d' % i, + 'name': 'A%d' % i, + 'desc': 'Address bus line %d' % i + } for i in range(16) + ) + ( + {'id': 'rnw', 'name': 'RNW', 'desc': 'Memory read or write'}, + {'id': 'sync', 'name': 'SYNC', 'desc': 'Sync - opcode fetch'}, + {'id': 'phi2', 'name': 'PHI2', 'desc': 'Phi2 clock, falling edge active'}, + ) + optional_channels = ( + {'id': 'rdy', 'name': 'RDY', 'desc': 'Ready, allows for wait states'}, + {'id': 'irq', 'name': 'IRQN', 'desc': 'Maskable interrupt'}, + {'id': 'nmi', 'name': 'NMIN', 'desc': 'Non-maskable interrupt'}, + {'id': 'rst', 'name': 'RSTN', 'desc': 'Reset'}, + ) + annotations = ( + ('data', 'Data bus'), + ('fetch', 'Fetch opcode'), + ('op1', 'Operand 1'), + ('op2', 'Operand 2'), + ('memrd', 'Memory Read'), + ('memwr', 'Memory Write'), + ('instr', 'Instruction'), + ('addr', 'Address'), + ) + annotation_rows = ( + ('databus', 'Data bus', (Ann.DATA,)), + ('cycle', 'Cycle', (Ann.FETCH, Ann.OP1, Ann.OP2, Ann.MEMRD, Ann.MEMWR)), + ('instructions', 'Instructions', (Ann.INSTR,)), + ('addrbus', 'Address bus', (Ann.ADDR,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.prev_phi2 = 0 # previous value of PHI2 + self.prev_sync = 0 # previous value of SYNC + self.samplenum_phi2_f = 0 # samplenr of PHI2 falling edge + self.samplenum_phi2_r = 0 # samplenr of PHI2 rising edge + self.samplenum_dasm_start = 0 # samplenr at start of disassembly + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + self.ann_data = None + + def decode(self): + opcount = 0 + cycle = Cycle.MEMRD + cycle_pr = cycle # cycle to Print + op = '???' + len = 0 + samplenum_datab = 0 # samplenumber at start of databyte print + samplenum_datae = 0 # samplenumber at end of databyte print + rnw_phi2_r = 0 # RNW line at rising edge of PHI2 + + while True: + pins = self.wait() + + bus_data = reduce_bus(pins[Pin.D0:Pin.D7+1]) # databus as byte + bus_addr = reduce_bus(pins[Pin.A0:Pin.A15+1]) # addressbus as word + + phi2f = 0 # 1 = PHI2 falling edge detected + phi2r = 0 # 1 = PHI2 rising edge detected + if pins[Pin.PHI2] == 1: + if self.prev_phi2 == 0: + phi2r = 1 # PHI2 rising edge detected + self.samplenum_phi2_r = self.samplenum + rnw_phi2_r = pins[Pin.RNW] # RNW line at rising-edge of PHI2 + # Print addressbus from PHI2 falling-edge to rising-edge + self.put(self.samplenum_phi2_f, self.samplenum, self.out_ann, [Ann.ADDR, [format(bus_addr, '04X')]]) + else: # PHI2 == 0 + if self.prev_phi2 == 1: + phi2f = 1 # PHI2 falling edge detected + self.samplenum_phi2_f = self.samplenum + # Print databus from PHI2 rising-edge to falling-edge + self.put(self.samplenum_phi2_r, self.samplenum, self.out_ann, [Ann.DATA, [format(bus_data, '02X')]]) + samplenum_datab = self.samplenum_phi2_r # needed for printing cycle-type + samplenum_datae = self.samplenum + if cycle == Cycle.DATA: # determine if the DATA cycle is READ or WRITE + if rnw_phi2_r == 1: + cycle_pr = Cycle.MEMRD # READ databyte + else: + cycle_pr = Cycle.MEMWR # write databyte + else: + cycle_pr = cycle # save cycle for print of addressing mode + self.prev_phi2 = pins[Pin.PHI2] # save PHI2 clock + + if pins[Pin.SYNC] == 0 and self.prev_sync == 1: + syncf = 1 # 1 = SYNC falling edge detected + else: + syncf = 0 + + if pins[Pin.SYNC] == 1 and self.prev_sync == 0: + syncr = 1 # 1 = SYNC rising edge detected + else: + syncr = 0 + self.prev_sync = pins[Pin.SYNC] # save SYNC value + + # ------------------------------------------------------------------------------------------------------------ + # SYNC = 1 if 6502 is doing an opcode FETCH, PHI rising edge is start of data-read + # ------------------------------------------------------------------------------------------------------------ + if pins[Pin.SYNC] == 1 and phi2r == 1: + cycle = Cycle.FETCH + op1 = 0 # value of 1st databyte + op2 = 0 # value of 2nd databyte (MSB) + self.samplenum_dasm_start = self.samplenum # start disassembly text from this samplenumber + + elif phi2f == 1: # from now on, read all data at falling-edge of PHI2 + # ------------------------------------------------------------------------------------------------------------ + # Read databyte op1 following the Fetch opcode byte, PHI2 falling edge is end of data-read + # ------------------------------------------------------------------------------------------------------------ + if cycle == Cycle.FETCH: + instr = instr_table[bus_data] # read instruction record + op = instr[0] # instruction mnemonic text + mode = instr[1] # instruction addressing mode + len = addr_mode_len_map[mode] # instruction number of bytes [1,2,3] + opcount = len - 1 # counter for number of bytes read, is now 0, 1 or 2 + if opcount > 0: + cycle = Cycle.OP1 + opcount -= 1 # decrement #databytes to read, is now 0 or 1 + else: + cycle = Cycle.DATA # next cycle is a data-byte cycle + + # ------------------------------------------------------------------------------------------------------------ + # Three situations are possible: + # opcount == 1: 3-byte instruction, read op2 databyte + # opcount == 0: a) 2-byte instruction with only 2 clock-cycles (Immediate mode and branches not taken mode). + # print the disassembly here, since the next PHI2 falling edge will be an opcode Fetch. + # b) 2-byte instruction with 3 or more clock-cycles. The disassembly is printed anyway, but + # will be overwritten by the disassembly print at the rising edge of SYNC + # ------------------------------------------------------------------------------------------------------------ + elif cycle == Cycle.OP1: + if mode == AddrMode.BRA: + op1 = signed_byte(bus_data) + else: + op1 = bus_data + if opcount > 0: # 3-byte instruction + cycle = Cycle.OP2 + opcount -= 1 + else: # 2-byte instruction + cycle = Cycle.DATA # next cycle is a data-byte cycle + if mode == AddrMode.BRA: + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op.format(op1+bus_addr)]]) + else: + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op.format(op1)]]) + + # ------------------------------------------------------------------------------------------------------------ + # Last databyte read of a 3-byte instruction. There's only one 3-byte instruction with only 3 clock-cycles, JMP $nnnn. + # Print the disassembly here, since the next PHI2 falling edge will be an opcode Fetch. Note that for other 3-byte + # instructions, this disassembly print is overwritten by the disassembly print at the rising edge of SYNC. + # ------------------------------------------------------------------------------------------------------------ + elif cycle == Cycle.OP2: + op2 = bus_data # read databyte + cycle = Cycle.DATA # next cycle is a data-byte cycle + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op.format(op1 + 256*op2)]]) + + # ------------------------------------------------------------------------------------------------------------ + # The rising edge of SYNC (which also coincides with the falling edge of PHI2) indicates the start of + # the next Fetch operation, so this is a good time to print the disassembly-text of the current instruction. + # ------------------------------------------------------------------------------------------------------------ + elif syncr == 1: # rising edge of SYNC + if len == 1: # 1-byte instructions + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op]]) + elif len == 2: # 2-byte instructions + if mode == AddrMode.BRA: # branch instructions with relative addressing + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op.format(op1+bus_addr)]]) + else: # all other 2-byte instructions + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op.format(op1)]]) + elif len == 3: # 3-byte instructions + self.put(self.samplenum_dasm_start, self.samplenum, self.out_ann, [Ann.INSTR, [op.format(op1 + 256*op2)]]) + + # ------------------------------------------------------------------------------------------------------------ + # Following a Fetch, OP1 or OP2 cycle, one or more byte read/write cycles may follow. They are identified here, + # but they don't influence the disassembly-text anymore. + # ------------------------------------------------------------------------------------------------------------ + else: # not a Fetch, Op1 or Op2 byte, so it is a DATA byte + cycle = Cycle.DATA # next cycle is a data-byte cycle + + # elif phi2f == 1: + + # ------------------------------------------------------------------------------------------------------------ + # Separate from above decoding, the cycle-type is printed here at the same time-stamp as the databyte + # ------------------------------------------------------------------------------------------------------------ + if phi2f == 1: + self.put(samplenum_datab, samplenum_datae, self.out_ann, [cycle_to_ann_map[cycle_pr], [cycle_to_name_map[cycle_pr]]]) + # while True: + # def decode(self): diff --git a/Software/decoders/mos6502/tables.py b/Software/decoders/mos6502/tables.py new file mode 100644 index 00000000..afc6fabb --- /dev/null +++ b/Software/decoders/mos6502/tables.py @@ -0,0 +1,316 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2017 David Banks +## Update 2025 by Emile : +## - added address- and data format-specifiers +## - removed non-6502 addressing modes +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' + 6502 Addressing Modes + + Map of Addressing Mode to Instruction Length + + Instruction tuple: (string, addressing mode) +''' + +# --------------------------------------------------- +# 6502 Addressing modes +# --------------------------------------------------- +class AddrMode: + IMP, IMPA, BRA, IMM, ZP, ZPX, ZPY, INDX, INDY, ABS, ABSX, ABSY, IND16 = range(13) + +# --------------------------------------------------- +# Map of Addressing Mode to Instruction Length +# --------------------------------------------------- +addr_mode_len_map = { + AddrMode.IMP: 1, # implied mode, just the opcode + AddrMode.IMPA: 1, # Accumulator mode + AddrMode.BRA: 2, # Branch type instructions, relative addressing + AddrMode.IMM: 2, # Immediate mode + AddrMode.ZP: 2, # Zero Page mode + AddrMode.ZPX: 2, # X-indexed Zero Page mode: $nn,Y + AddrMode.ZPY: 2, # Y-indexed Zero Page mode: $nn,X + AddrMode.INDX: 2, # X-indexed Zero Page indirect mode: ($nn,X) + AddrMode.INDY: 2, # Zero Page Indirect Y-indexed mode: ($nn),Y + AddrMode.ABS: 3, # Absolute: $nnnn + AddrMode.ABSX: 3, # X-Indexed Absolute: $nnnn,X + AddrMode.ABSY: 3, # Y-indexed Absolute: $nnnn,Y + AddrMode.IND16: 3, # Absolute Indirect: ($nnnn) +} + +# --------------------------------------------------- +# Instruction tuple: (string, addressing mode) +# --------------------------------------------------- +instr_table = { + 0x00: ( 'BRK' , AddrMode.IMP ), + 0x01: ( 'ORA (${:02X},X)', AddrMode.INDX ), # ZP + 0x02: ( '???' , AddrMode.IMP ), + 0x03: ( '???' , AddrMode.IMP ), + 0x04: ( '???' , AddrMode.IMP ), + 0x05: ( 'ORA ${:02X}' , AddrMode.ZP ), # ZP + 0x06: ( 'ASL ${:02X}' , AddrMode.ZP ), # ZP + 0x07: ( '???' , AddrMode.IMP ), + 0x08: ( 'PHP' , AddrMode.IMP ), + 0x09: ( 'ORA #${:02X}' , AddrMode.IMM ), # ZP + 0x0A: ( 'ASL A' , AddrMode.IMPA ), + 0x0B: ( '???' , AddrMode.IMP ), + 0x0C: ( '???' , AddrMode.ABS ), + 0x0D: ( 'ORA ${:04X}' , AddrMode.ABS ), # A16 + 0x0E: ( 'ASL ${:04X}' , AddrMode.ABS ), # A16 + 0x0F: ( '???' , AddrMode.IMP ), + 0x10: ( 'BPL ${:04X}' , AddrMode.BRA ), + 0x11: ( 'ORA (${:02X}),Y', AddrMode.INDY ), # ZP + 0x12: ( '???' , AddrMode.IMP ), + 0x13: ( '???' , AddrMode.IMP ), + 0x14: ( '???' , AddrMode.IMP ), + 0x15: ( 'ORA ${:02X},X' , AddrMode.ZPX ), # ZP + 0x16: ( 'ASL ${:02X},X' , AddrMode.ZPX ), # ZP + 0x17: ( '???' , AddrMode.IMP ), + 0x18: ( 'CLC' , AddrMode.IMP ), + 0x19: ( 'ORA ${:04X},Y' , AddrMode.ABSY ), # A16 + 0x1A: ( '???' , AddrMode.IMP ), + 0x1B: ( '???' , AddrMode.IMP ), + 0x1C: ( '???' , AddrMode.IMP ), + 0x1D: ( 'ORA ${:04X},X' , AddrMode.ABSX ), # A16 + 0x1E: ( 'ASL ${:04X},X' , AddrMode.ABSX ), # A16 + 0x1F: ( '???' , AddrMode.IMP ), + 0x20: ( 'JSR ${:04X}' , AddrMode.ABS ), + 0x21: ( 'AND (${:02X},X)', AddrMode.INDX ), # ZP + 0x22: ( '???' , AddrMode.IMP ), + 0x23: ( '???' , AddrMode.IMP ), + 0x24: ( 'BIT ${:02X}' , AddrMode.ZP ), # ZP + 0x25: ( 'AND ${:02X}' , AddrMode.ZP ), # ZP + 0x26: ( 'ROL ${:02X}' , AddrMode.ZP ), # ZP + 0x27: ( '???' , AddrMode.IMP ), + 0x28: ( 'PLP' , AddrMode.IMP ), + 0x29: ( 'AND #${:02X}' , AddrMode.IMM ), + 0x2A: ( 'ROL A' , AddrMode.IMPA ), + 0x2B: ( '???' , AddrMode.IMP ), + 0x2C: ( 'BIT ${:04X}' , AddrMode.ABS ), # A16 + 0x2D: ( 'AND ${:04X}' , AddrMode.ABS ), # A16 + 0x2E: ( 'ROL ${:04X}' , AddrMode.ABS ), # A16 + 0x2F: ( '???' , AddrMode.IMP ), + 0x30: ( 'BMI ${:04X}' , AddrMode.BRA ), + 0x31: ( 'AND (${:02X}),Y', AddrMode.INDY ), # ZP + 0x32: ( '???' , AddrMode.IMP ), + 0x33: ( '???' , AddrMode.IMP ), + 0x34: ( '???' , AddrMode.IMP ), + 0x35: ( 'AND ${:02X},X' , AddrMode.ZPX ), # ZP + 0x36: ( 'ROL ${:02X},X' , AddrMode.ZPX ), # ZP + 0x37: ( '???' , AddrMode.IMP ), + 0x38: ( 'SEC' , AddrMode.IMP ), + 0x39: ( 'AND ${:04X},Y' , AddrMode.ABSY ), # A16 + 0x3A: ( '???' , AddrMode.IMP ), + 0x3B: ( '???' , AddrMode.IMP ), + 0x3C: ( '???' , AddrMode.IMP ), + 0x3D: ( 'AND ${:04X},X' , AddrMode.ABSX ), # A16 + 0x3E: ( 'ROL ${:04X},X' , AddrMode.ABSX ), # A16 + 0x3F: ( '???' , AddrMode.IMP ), + 0x40: ( 'RTI' , AddrMode.IMP ), + 0x41: ( 'EOR (${:02X}),X', AddrMode.INDX ), # ZP + 0x42: ( '???' , AddrMode.IMP ), + 0x43: ( '???' , AddrMode.IMP ), + 0x44: ( '???' , AddrMode.IMP ), + 0x45: ( 'EOR ${:02X}' , AddrMode.ZP ), # ZP + 0x46: ( 'LSR ${:02X}' , AddrMode.ZP ), # ZP + 0x47: ( '???' , AddrMode.IMP ), + 0x48: ( 'PHA' , AddrMode.IMP ), + 0x49: ( 'EOR #${:02X}' , AddrMode.IMM ), + 0x4A: ( 'LSR A' , AddrMode.IMPA ), + 0x4B: ( '???' , AddrMode.IMP ), + 0x4C: ( 'JMP ${:04X}' , AddrMode.ABS ), # A16 + 0x4D: ( 'EOR ${:04X}' , AddrMode.ABS ), # A16 + 0x4E: ( 'LSR ${:04X}' , AddrMode.ABS ), # A16 + 0x4F: ( '???' , AddrMode.IMP ), + 0x50: ( 'BVC ${:04X}' , AddrMode.BRA ), + 0x51: ( 'EOR (${:02X}),Y', AddrMode.INDY ), # ZP + 0x52: ( '???' , AddrMode.IMP ), + 0x53: ( '???' , AddrMode.IMP ), + 0x54: ( '???' , AddrMode.IMP ), + 0x55: ( 'EOR ${:02X},X' , AddrMode.ZPX ), # ZP + 0x56: ( 'LSR ${:02X},X' , AddrMode.ZPX ), # ZP + 0x57: ( '???' , AddrMode.IMP ), + 0x58: ( 'CLI' , AddrMode.IMP ), + 0x59: ( 'EOR ${:04X},Y' , AddrMode.ABSY ), # A16 + 0x5A: ( '???' , AddrMode.IMP ), + 0x5B: ( '???' , AddrMode.IMP ), + 0x5C: ( '???' , AddrMode.IMP ), + 0x5D: ( 'EOR ${:04X},X' , AddrMode.ABSX ), # A16 + 0x5E: ( 'LSR ${:04X},X' , AddrMode.ABSX ), # A16 + 0x5F: ( '???' , AddrMode.IMP ), + 0x60: ( 'RTS' , AddrMode.IMP ), + 0x61: ( 'ADC (${:02X},X)', AddrMode.INDX ), # ZP + 0x62: ( '???' , AddrMode.IMP ), + 0x63: ( '???' , AddrMode.IMP ), + 0x64: ( '???' , AddrMode.IMP ), + 0x65: ( 'ADC ${:02X}' , AddrMode.ZP ), # ZP + 0x66: ( 'ROR ${:02X}' , AddrMode.ZP ), # ZP + 0x67: ( '???' , AddrMode.IMP ), + 0x68: ( 'PLA' , AddrMode.IMP ), + 0x69: ( 'ADC #${:02X}' , AddrMode.IMM ), + 0x6A: ( 'ROR A' , AddrMode.IMPA ), + 0x6B: ( '???' , AddrMode.IMP ), + 0x6C: ( 'JMP (${:04X})' , AddrMode.IND16), # (A16) + 0x6D: ( 'ADC ${:04X}' , AddrMode.ABS ), # A16 + 0x6E: ( 'ROR ${:04X}' , AddrMode.ABS ), # A16 + 0x6F: ( '???' , AddrMode.IMP ), + 0x70: ( 'BVS ${:04X}' , AddrMode.BRA ), + 0x71: ( 'ADC (${:02X}),Y', AddrMode.INDY ), # ZP + 0x72: ( '???' , AddrMode.IMP ), + 0x73: ( '???' , AddrMode.IMP ), + 0x74: ( '???' , AddrMode.IMP ), + 0x75: ( 'ADC ${:02X},X' , AddrMode.ZPX ), # ZP + 0x76: ( 'ROR ${:02X},X' , AddrMode.ZPX ), # ZP + 0x77: ( '???' , AddrMode.IMP ), + 0x78: ( 'SEI' , AddrMode.IMP ), + 0x79: ( 'ADC ${:04X},Y' , AddrMode.ABSY ), # A16 + 0x7A: ( '???' , AddrMode.IMP ), + 0x7B: ( '???' , AddrMode.IMP ), + 0x7C: ( '???' , AddrMode.IMP ), + 0x7D: ( 'ADC ${:04X},X' , AddrMode.ABSX ), # A16 + 0x7E: ( 'ROR ${:04X},X' , AddrMode.ABSX ), # A16 + 0x7F: ( '???' , AddrMode.IMP ), + 0x80: ( '???' , AddrMode.IMP ), + 0x81: ( 'STA (${:02X},X)', AddrMode.INDX ), # ZP + 0x82: ( '???' , AddrMode.IMP ), + 0x83: ( '???' , AddrMode.IMP ), + 0x84: ( 'STY ${:02X}' , AddrMode.ZP ), # ZP + 0x85: ( 'STA ${:02X}' , AddrMode.ZP ), # ZP + 0x86: ( 'STX ${:02X}' , AddrMode.ZP ), # ZP + 0x87: ( '???' , AddrMode.IMP ), + 0x88: ( 'DEY' , AddrMode.IMP ), + 0x89: ( '???' , AddrMode.IMP ), + 0x8A: ( 'TXA' , AddrMode.IMP ), + 0x8B: ( '???' , AddrMode.IMP ), + 0x8C: ( 'STY ${:04X}' , AddrMode.ABS ), # A16 + 0x8D: ( 'STA ${:04X}' , AddrMode.ABS ), # A16 + 0x8E: ( 'STX ${:04X}' , AddrMode.ABS ), # A16 + 0x8F: ( '???' , AddrMode.IMP ), + 0x90: ( 'BCC ${:04X}' , AddrMode.BRA ), + 0x91: ( 'STA (${:02X}),Y', AddrMode.INDY ), # ZP + 0x92: ( '???' , AddrMode.IMP ), + 0x93: ( '???' , AddrMode.IMP ), + 0x94: ( 'STY ${:02X},X' , AddrMode.ZPX ), # ZP + 0x95: ( 'STA ${:02X},X' , AddrMode.ZPX ), # ZP + 0x96: ( 'STX ${:02X},Y' , AddrMode.ZPY ), # ZP + 0x97: ( '???' , AddrMode.IMP ), + 0x98: ( 'TYA' , AddrMode.IMP ), + 0x99: ( 'STA ${:04X},Y' , AddrMode.ABSY ), # A16 + 0x9A: ( 'TXS' , AddrMode.IMP ), + 0x9B: ( '???' , AddrMode.IMP ), + 0x9C: ( '???' , AddrMode.IMP ), + 0x9D: ( 'STA ${:04X},X' , AddrMode.ABSX ), # A16 + 0x9E: ( '???' , AddrMode.IMP ), + 0x9F: ( '???' , AddrMode.IMP ), + 0xA0: ( 'LDY #${:02X}' , AddrMode.IMM ), + 0xA1: ( 'LDA (${:02X},X)', AddrMode.INDX ), # ZP + 0xA2: ( 'LDX #${:02X}' , AddrMode.IMM ), + 0xA3: ( '???' , AddrMode.IMP ), + 0xA4: ( 'LDY ${:02X}' , AddrMode.ZP ), # ZP + 0xA5: ( 'LDA ${:02X}' , AddrMode.ZP ), # ZP + 0xA6: ( 'LDX ${:02X}' , AddrMode.ZP ), # ZP + 0xA7: ( '???' , AddrMode.IMP ), + 0xA8: ( 'TAY' , AddrMode.IMP ), + 0xA9: ( 'LDA #${:02X}' , AddrMode.IMM ), + 0xAA: ( 'TAX' , AddrMode.IMP ), + 0xAB: ( '???' , AddrMode.IMP ), + 0xAC: ( 'LDY ${:04X}' , AddrMode.ABS ), # A16 + 0xAD: ( 'LDA ${:04X}' , AddrMode.ABS ), # A16 + 0xAE: ( 'LDX ${:04X}' , AddrMode.ABS ), # A16 + 0xAF: ( '???' , AddrMode.IMP ), + 0xB0: ( 'BCS ${:04X}' , AddrMode.BRA ), + 0xB1: ( 'LDA (${:02X}),Y', AddrMode.INDY ), # ZP + 0xB2: ( '???' , AddrMode.IMP ), + 0xB3: ( '???' , AddrMode.IMP ), + 0xB4: ( 'LDY ${:02X},X' , AddrMode.ZPX ), # ZP + 0xB5: ( 'LDA ${:02X},X' , AddrMode.ZPX ), # ZP + 0xB6: ( 'LDX ${:02X},Y' , AddrMode.ZPY ), # ZP + 0xB7: ( '???' , AddrMode.IMP ), + 0xB8: ( 'CLV' , AddrMode.IMP ), + 0xB9: ( 'LDA ${:04X},Y' , AddrMode.ABSY ), # A16 + 0xBA: ( 'TSX' , AddrMode.IMP ), + 0xBB: ( '???' , AddrMode.IMP ), + 0xBC: ( 'LDY ${:04X},X' , AddrMode.ABSX ), # A16 + 0xBD: ( 'LDA ${:04X},Y' , AddrMode.ABSX ), # A16 + 0xBE: ( 'LDX ${:04X},Y' , AddrMode.ABSY ), # A16 + 0xBF: ( '???' , AddrMode.IMP ), + 0xC0: ( 'CPY #${:02X}' , AddrMode.IMM ), + 0xC1: ( 'CMP (${:02X},X)', AddrMode.INDX ), # ZP + 0xC2: ( '???' , AddrMode.IMP ), + 0xC3: ( '???' , AddrMode.IMP ), + 0xC4: ( 'CPY ${:02X}' , AddrMode.ZP ), # ZP + 0xC5: ( 'CMP ${:02X}' , AddrMode.ZP ), # ZP + 0xC6: ( 'DEC ${:02X}' , AddrMode.ZP ), # ZP + 0xC7: ( '???' , AddrMode.IMP ), + 0xC8: ( 'INY' , AddrMode.IMP ), + 0xC9: ( 'CMP #${:02X}' , AddrMode.IMM ), + 0xCA: ( 'DEX' , AddrMode.IMP ), + 0xCB: ( '???' , AddrMode.IMP ), + 0xCC: ( 'CPY ${:04X}' , AddrMode.ABS ), # A16 + 0xCD: ( 'CMP ${:04X}' , AddrMode.ABS ), # A16 + 0xCE: ( 'DEC ${:04X}' , AddrMode.ABS ), # A16 + 0xCF: ( '???' , AddrMode.IMP ), + 0xD0: ( 'BNE ${:04X}' , AddrMode.BRA ), + 0xD1: ( 'CMP (${:02X}),Y', AddrMode.INDY ), # ZP + 0xD2: ( '???' , AddrMode.IMP ), + 0xD3: ( '???' , AddrMode.IMP ), + 0xD4: ( '???' , AddrMode.IMP ), + 0xD5: ( 'CMP ${:02X},X' , AddrMode.ZPX ), # ZP + 0xD6: ( 'DEC ${:02X},X' , AddrMode.ZPX ), # ZP + 0xD7: ( '???' , AddrMode.IMP ), + 0xD8: ( 'CLD' , AddrMode.IMP ), + 0xD9: ( 'CMP ${:04X},Y' , AddrMode.ABSY ), # A16 + 0xDA: ( '???' , AddrMode.IMP ), + 0xDB: ( '???' , AddrMode.IMP ), + 0xDC: ( '???' , AddrMode.IMP ), + 0xDD: ( 'CMP ${:04X},X' , AddrMode.ABSX ), # A16 + 0xDE: ( 'DEC ${:04X},X' , AddrMode.ABSX ), # A16 + 0xDF: ( '???' , AddrMode.IMP ), + 0xE0: ( 'CPX #${:02X}' , AddrMode.IMM ), + 0xE1: ( 'SBC (${:02X},X)', AddrMode.INDX ), # ZP + 0xE2: ( '???' , AddrMode.IMP ), + 0xE3: ( '???' , AddrMode.IMP ), + 0xE4: ( 'CPX ${:02X}' , AddrMode.ZP ), # ZP + 0xE5: ( 'SBC ${:02X}' , AddrMode.ZP ), # ZP + 0xE6: ( 'INC ${:02X}' , AddrMode.ZP ), # ZP + 0xE7: ( '???' , AddrMode.IMP ), + 0xE8: ( 'INX' , AddrMode.IMP ), + 0xE9: ( 'SBC #${:02X}' , AddrMode.IMM ), + 0xEA: ( 'NOP' , AddrMode.IMP ), + 0xEB: ( '???' , AddrMode.IMP ), + 0xEC: ( 'CPX ${:04X}' , AddrMode.ABS ), # A16 + 0xED: ( 'SBC ${:04X}' , AddrMode.ABS ), # A16 + 0xEE: ( 'INC ${:04X}' , AddrMode.ABS ), # A16 + 0xEF: ( '???' , AddrMode.IMP ), + 0xF0: ( 'BEQ ${:04X}' , AddrMode.BRA ), + 0xF1: ( 'SBC (${:02X}),Y', AddrMode.INDY ), + 0xF2: ( '???' , AddrMode.IMP ), + 0xF3: ( '???' , AddrMode.IMP ), + 0xF4: ( '???' , AddrMode.IMP ), + 0xF5: ( 'SBC ${:02X},X' , AddrMode.ZPX ), # ZP + 0xF6: ( 'INC ${:02X},X' , AddrMode.ZPX ), # ZP + 0xF7: ( '???' , AddrMode.IMP ), + 0xF8: ( 'SED' , AddrMode.IMP ), + 0xF9: ( 'SBC ${:04X},Y' , AddrMode.ABSY ), # A16 + 0xFA: ( '???' , AddrMode.IMP ), + 0xFB: ( '???' , AddrMode.IMP ), + 0xFC: ( '???' , AddrMode.IMP ), + 0xFD: ( 'SBC ${:04X},X' , AddrMode.ABSX ), # A16 + 0xFE: ( 'INC ${:04X},X' , AddrMode.ABSX ), # A16 + 0xFF: ( '???' , AddrMode.IMP ), +} diff --git a/Software/decoders/pwm/pd.py b/Software/decoders/pwm/pd.py index 2eda02ca..3a415369 100644 --- a/Software/decoders/pwm/pd.py +++ b/Software/decoders/pwm/pd.py @@ -43,10 +43,12 @@ class Decoder(srd.Decoder): annotations = ( ('duty-cycle', 'Duty cycle'), ('period', 'Period'), + ('frequency', 'Frequency'), ) annotation_rows = ( ('duty-cycle-vals', 'Duty cycles', (0,)), ('periods', 'Periods', (1,)), + ('frequency-vals', 'Frequencies', (2,)), ) binary = ( ('raw', 'RAW file'), @@ -90,6 +92,29 @@ def putp(self, period_t): self.put(self.ss_block, self.es_block, self.out_ann, [1, [period_s]]) + def putf(self, period_t): + if period_t != 0: + frequency = 1 / period_t + + # Adjust granularity. + + if frequency >= 1e15: + frequency_s = '%.3f PHz' % (frequency / 1e15) + elif frequency >= 1e12: + frequency_s = '%.3f THz' % (frequency / 1e12) + elif frequency >= 1e9: + frequency_s = '%.3f GHz' % (frequency / 1e9) + elif frequency >= 1e6: + frequency_s = '%.3f MHz' % (frequency / 1e6) + elif frequency >= 1e3: + frequency_s = '%.3f kHz' % (frequency / 1e3) + else: + frequency_s = '%.3f Hz' % (frequency) + + self.put(self.ss_block, self.es_block, self.out_ann, [2, [frequency_s]]) + else: + self.put(self.ss_block, self.es_block, self.out_ann, [2, ["invalid"]]) + def putb(self, data): self.put(self.ss_block, self.es_block, self.out_binary, data) @@ -133,6 +158,7 @@ def decode(self): # Report the period in units of time. period_t = float(period / self.samplerate) self.putp(period_t) + self.putf(period_t) # Update and report the new duty cycle average. num_cycles += 1 diff --git a/Software/decoders/sigrokdecode.py b/Software/decoders/sigrokdecode.py deleted file mode 100644 index ed007a60..00000000 --- a/Software/decoders/sigrokdecode.py +++ /dev/null @@ -1,36 +0,0 @@ -SRD_CONF_SAMPLERATE = 0 -OUTPUT_ANN = 0 -OUTPUT_PYTHON = 1 -OUTPUT_BINARY = 2 -OUTPUT_LOGIC = 3 -OUTPUT_META = 4 - -class Decoder: - - cObj = None - inputs = [] - outputs = [] - tags = [] - channels = () - optional_channels = () - options = () - annotations = () - annotation_rows = () - binary = () - samplenum = 0 - matched = () - - def has_channel(self, channel): - return self.cObj.HasChannel(channel) - - def wait(self, conds = None): - result = self.cObj.Wait(conds) - if result == None: - raise Exception("Terminated") - return result - - def put(self, startsample, endsample, output_id, data): - self.cObj.Put(startsample, endsample, output_id, data) - - def register(self, output_type, proto_id = None, meta = None): - return self.cObj.Register(output_type, meta) \ No newline at end of file diff --git a/Software/decoders/smartcard/pd.py b/Software/decoders/smartcard/pd.py deleted file mode 100644 index 18561ca2..00000000 --- a/Software/decoders/smartcard/pd.py +++ /dev/null @@ -1,614 +0,0 @@ -## -## Copyright (C) 2020 Sven Soltermann -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, see . -## - -import sigrokdecode as srd -import struct -import sys -import codecs -import ctypes - -class pcap_udp_pkt(): - # GSM TAP - h = b'' - - # layer_2 - h += b'\x00\x00\x00\x00\x00\x00' # destination_mac - h += b'\x00\x00\x00\x00\x00\x00' # source_mac - h += b'\x08\x00' # layer_3_protocol - - # layer_3 - h += b'\x45' # version - h += b'\x00' # DiffServField - h += b'\xFF\xFF' # total_length - h += b'\x2B\x0D' # Identification - h += b'\x40\x00' # Flags - h += b'\x40' # TTL - h += b'\x11' # layer_4_protocol - h += b'\x00\x00' # header_checksum - h += b'\x7F\x00\x00\x01' # source_ip - h += b'\x7F\x00\x00\x01' # dest_ip - - # layer_4 - h += b'\xcc\x46' # source_port - h += b'\x12\x79' # dest_port - h += b'\x00\x00' # datagram_length - h += b'\x00\x00' # checksum - - def __init__(self, ts, data): - self.header = bytearray(pcap_udp_pkt.h) - self.data = b'' - self.set_timestamp(ts) - self.set_data(data) - - def set_timestamp(self, ts): - self.timestamp = ts - - def set_data(self, data): - self.data = list(b'\x02\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + bytes(data)); - self.header[16:18] = struct.pack('>H', len(self.data) + 28) - self.header[38:40] = struct.pack('>H', len(self.data) + 8) - - def packet(self): - return bytes(self.header) + bytes(self.data) - - def record_header(self): - # See https://wiki.wireshark.org/Development/LibpcapFileFormat. - (secs, usecs) = self.timestamp - h = struct.pack(' 0 ): - self.sampleOverflowCount += 1 - self.lastSamplePositive = True - if (self.peeked_byte != None): - byte =self.peeked_byte - self.peeked_byte = None - self.peeked_samplenum = -1 - return byte; - self.wait({1: 'f'}) - self.sleep_cycles() - return self.read_byte_no_wait(); - - def peek_byte(self): - self.wait({1: 'f'}) - self.sleep_cycles() - self.peeked_samplenum = self.samplenum - self.peeked_byte = self.read_byte_no_wait(); - return self.peeked_byte; - - def read_byte_no_wait(self): - self.bits = [] - self.ss = self.samplenum - for x in range(10): - pins = self.wait({'skip': 0}) - #if (x != 0 and x < 9): - #self.log("read_bit: " + str(pins[1]) + " sample: "+ str(self.samplenum)) - self.bits.append(pins[1]) - if (self.sample_as_clock): - self.wait({'skip': self.clock_skip - 4}) - else: - for c in range(int(self.clock_skip)): - self.wait({0: 'r'}) - - self.es = self.samplenum - if (self.bits.count(1) % 2 != 0): - self.log(self.samplenum, "CHKSUM ERROR: ", pins[1], "bits: ", self.bits) - self.put(self.ss, self.samplenum, self.out_ann, [0, ["CHKSUM ERROR bits={bits}".format(bits=self.bits)]]) - byte = self.get_bytes(self.bits[1:9]) - self.log(self.samplenum, self.bits[1:9], " : ", "0x{:02x}".format(byte)) - self.put(self.ss, self.samplenum, self.out_ann, [1, [hex(byte)]]) - return byte - - - def sleep_cycles(self): - if (self.sample_as_clock): - self.wait({'skip': int(self.clock_skip / 3)}) - else: - for c in range(int(self.clock_skip / 3)): - self.wait({0: 'r'}) - - def handle_atr(self, pins): - - atr_start = self.samplenum; - - self.log("START ATR:", self.samplenum); - self.state = 'ATR' - - tA = [] - tB = [] - tC = [] - tD = [] - historicalBytes = [] - self.ATR = [] - - if (self.peeked_byte != None): - atr_start = self.peeked_samplenum - byte = self.read_byte() - else: - byte = self.read_first_byte() - self.ATR.append(byte) - - t0 = self.read_byte() - self.ATR.append(t0) - - firstT0 = t0 - - self.hasT0 = False - self.hasT1 = False - self.hasT15 = False - while (firstT0 & 0b11110000): - if (firstT0 & 0b00010000): - byte = self.read_byte() - tA.append(byte) - self.ATR.append(byte) - if (firstT0 & 0b00100000): - byte = self.read_byte() - tB.append(byte) - self.ATR.append(byte) - if (firstT0 & 0b01000000): - byte = self.read_byte() - tC.append(byte) - self.ATR.append(byte) - if (firstT0 & 0b10000000): - byte = self.read_byte() - if ((byte & 0x0F) == 0): - self.hasT0 = True - elif ((byte & 0x0F) == 1): - self.hasT1 = True - elif ((byte & 0x0F) == 15): - self.hasT15 = True - else: - self.log("Invalid Protocol in ATR: ", "T=",(byte & 0x0F)) - self.put(atr_start, self.samplenum, self.out_ann, [0, ["Invalid Protocol in ATR T={protocol}".format(protocol=(byte & 0x0F))]]) - tD.append(byte) - self.ATR.append(byte) - firstT0 = byte - self.log("TD("+str(len(tD))+"): ", hex(firstT0), "T=",(byte & 0x0F)) - else: - firstT0 = 0; - - for _ in range(0, t0 & 0x0F): - byte = self.read_byte() - self.ATR.append(byte) - - if (self.hasT1 == False and self.hasT0 == False): - self.hasT0 = True - - if (self.hasT1 == True or self.hasT15): - byte = self.read_byte() - self.ATR.append(byte) - xor = 0 - for i in range(1, len(self.ATR)): xor = xor ^ self.ATR[i] - if (xor != 0): - self.put(atr_start, self.samplenum, self.out_ann, [0, ["Invalid TCK in ATR, got={tck:02x} expected={xor:02x}".format(tck=byte,xor=xor)]]) - self.log("Invalid TCK in ATR", hex(byte), hex(xor)) - - self.put(atr_start, self.samplenum, self.out_ann, [2, ["ATR", "ATR={atr}".format(atr=codecs.encode(bytes(self.ATR), 'hex'))]]) - self.put(atr_start, self.samplenum, self.out_python, [0, self.ATR]) - - self.log("ENDATR", codecs.encode(bytes(self.ATR), 'hex')) - self.state = 'DATA' - - if (self.options['protocol'] == "T=0"): - self.hasT0 = True - self.hasT1 = False - elif (self.options['protocol'] == "T=1"): - self.hasT0 = False - self.hasT1 = True - - def t1_parse_block(self, es): - packet = [] - lrc = 0; - nad = self.read_byte() - lrc = lrc ^ nad - self.put(es, self.samplenum, self.out_ann, [1, "NAD:" + hex(nad)]) - sad = nad & 0x70 - dad = nad & 0x07 - self.log("T=1 NAD=", hex(nad), "SAD=", hex(sad), "DAD=", hex(dad)) - - pcb = self.read_byte() - lrc = lrc ^ pcb - - isSBlock = False - isRBlock = False - isIBlock = False - if (pcb & 0b11000000 == 0b11000000): - # S-Block - isSBlock = True - self.put(es, self.samplenum, self.out_ann, [3, "PCB S " + hex(pcb)]) - self.log("PCB S-Block", hex(pcb & 0b00111111)) - elif (pcb & 0b10000000 == 0b10000000): - # R-Block - isRBlock = True - self.put(es, self.samplenum, self.out_ann, [3, "PCB R " + hex(pcb)]) - self.log("PCB R-Block", hex(pcb & 0b00111111)) - else: - # I-Block - isIBlock = True - self.put(es, self.samplenum, self.out_ann, [3, "PCB I " + hex(pcb)]) - self.log("PCB I-Block", hex(pcb)) - - bLen = self.read_byte() - lrc = lrc ^ bLen - if (bLen > 0): - for _ in range(bLen): - byte = self.read_byte() - lrc = lrc ^ byte - if (isIBlock): packet.append(byte) - - # CRC to implement - bLrc = self.read_byte() - lrc = lrc ^ bLrc - - if (lrc != 0): - self.put(es, self.samplenum, self.out_ann, [0, ["Invalid checksum on T=1 block, , got={got:02x} expected={expected:02x}".format(got=lrc,expected=bLrc)]]) - self.log("Invalid checksum on T=1 block", hex(lrc), hex(bLrc)) - - self.log("block_content", codecs.encode(bytes(packet), 'hex')) - - if (isIBlock): - self.put(es, self.samplenum, self.out_ann, [6, ["I-Block", "I-Block len={len} isMultiBlock={multi}".format(len=bLen,multi=(pcb & 0b00100000 > 0))]]) - if (isRBlock): - self.put(es, self.samplenum, self.out_ann, [7, ["R-Block", "R-Block flag={flag:02x}".format(flag=pcb & 0b00111111)]]) - if (isSBlock): - self.put(es, self.samplenum, self.out_ann, [8, ["S-Block", "S-Block flag={flag:02x}".format(flag=pcb & 0b00111111)]]) - - if (isIBlock and pcb & 0b00100000): # m-flag - self.log("T=1 Multiblock flag", hex(pcb)) - while (True): - isIBlock2,packet2 = self.t1_parse_block(self.samplenum) - if (isIBlock2): - packet = packet + packet2 - break - - - return isIBlock,packet; - - def handle_pps(self): - lrc = 0 - ss = self.peeked_samplenum - pps = self.read_byte() - pps0 = self.read_byte() - pps1 = 0; pps2 = 0; pps3 = 0 - if (pps0 & 0b00010000): - pps1 = self.read_byte() - lrc = lrc ^ pps1 - if (pps0 & 0b00100000): - pps2 = self.read_byte() - lrc = lrc ^ pps2 - if (pps0 & 0b01000000): - pps3 = self.read_byte() - lrc = lrc ^ pps3 - pck = self.read_byte() - lrc = lrc ^ pps ^ pps0 ^ pck - if (lrc != 0): - self.put(ss, self.samplenum, self.out_ann, [0, ["INVALID Checksum on PPS Request, got={got:02x} expected={expected:02x}".format(got=pck,expected=(lrc ^ pps ^ pps0))]]) - self.log("INVALID Checksum on PPS Request", hex(lrc)) - - r_lrc = 0 - r_pps = self.read_byte() - r_pps1 = 0; r_pps2 = 0; r_pps3 = 0 - if (r_pps != 0xFF): - self.put(ss, self.samplenum, self.out_ann, [0, ["PPS Request not confirmed"]]) - self.log("PPS Request not confirmed", r_pps) - r_pps0 = self.read_byte() - if (r_pps0 & 0b00010000): - r_pps1 = self.read_byte() - r_lrc = r_lrc ^ r_pps1 - if (r_pps0 & 0b00100000): - r_pps2 = self.read_byte() - r_lrc = r_lrc ^ r_pps2 - if (r_pps0 & 0b01000000): - r_pps3 = self.read_byte() - r_lrc = r_lrc ^ r_pps3 - r_pck = self.read_byte() - r_lrc = r_lrc ^ r_pps ^ r_pps0 ^ r_pck - if (r_lrc != 0): - self.put(ss, self.samplenum, self.out_ann, [0, ["INVALID Checksum on PPS Response, got={got:02x} expected={expected:02x}".format(got=r_pck,expected=(r_lrc ^ r_pps ^ r_pps0))]]) - self.log("INVALID Checksum on PPS Response", hex(r_lrc)) - - if (pps0 == r_pps0 and pps1 == r_pps1 and pps2 == r_pps2 and pps3 == r_pps3): - if (self.detect_clock or self.sample_as_clock): - tmp_fi = self.clock_rate[int(pps1 >> 4)] - tmp_di = self.baud_rate[int(pps1 & 0x0F)] - tmp_clock_skip = int(tmp_fi / tmp_di) - self.log("Received PPS change: FI", tmp_fi, "DI", tmp_di, "clock_skip", tmp_clock_skip) - self.clock_skip = int(tmp_clock_skip * self.detected_clock_skip / 372) - self.fi = tmp_fi - self.di = tmp_di - self.log("PPS Success new settings (calculated): FI", self.fi, "DI", self.di, "clock_skip", self.clock_skip) - else: - self.fi = self.clock_rate[int(pps1 >> 4)] - self.di = self.baud_rate[int(pps1 & 0x0F)] - self.clock_skip = int(self.fi / self.di) - self.log("PPS Success new settings: FI", self.fi, "DI", self.di, "clock_skip", self.clock_skip) - else: - self.log("INVALID PPS. Request & Response not matching.", hex(r_lrc)) - self.put(ss, self.samplenum, self.out_ann, [0, ["INVALID PPS. Request & Response not matching"]]) - self.put(ss, self.samplenum, self.out_ann, [3, ["PPS", "PPS DI={di} FI={fi} clock_skip={clock_skip}".format(di=self.di,fi=self.fi,clock_skip=self.clock_skip)]]) - - - def decode(self): - self.write_pcap_header(); - while True: - # State machine. - if self.state == 'FIND START': - self.wait({1: 'h'}) - self.handle_atr(self.wait({1: 'f'})) - elif self.state == 'DATA': - packet = []; - - firstByte = self.peek_byte(); - if (firstByte == 0xFF): # PPS Request - self.handle_pps(); - continue; - elif (firstByte == 0x3b): # Probably ATR - self.handle_atr(self.wait({'skip': 0})) - continue; - - es = self.peeked_samplenum; - if (self.hasT0): - bClass = self.read_byte() - packet.append(bClass); # class - bIns = self.read_byte(); - packet.append(bIns); # instruction - packet.append(self.read_byte()); # param1 - packet.append(self.read_byte()); # param2 - dataLen = self.read_byte(); - self.log("DATALEN: ", dataLen) - packet.append(dataLen); # param3 - procedureByte = self.read_byte(); - if (procedureByte == bIns): - for _ in range(0,dataLen): - packet.append(self.read_byte()); # payload - packet.append(self.read_byte()); # status0 - packet.append(self.read_byte()); # status1 - elif (procedureByte == 0x60): - packet.append(procedureByte); # status0 - packet.append(self.read_byte()); # status1 - elif (procedureByte & 0xF0 == 0x60 or procedureByte & 0xF0 == 0x90): - packet.append(procedureByte); # status0 - packet.append(self.read_byte()); # status1 - else: - self.put(es, self.samplenum, self.out_ann, [0, ["INVALID Procedure Byte"]]) - self.log("INVALID Procedure Byte", hex(procedureByte)) - self.put(es, self.samplenum, self.out_ann, [4, ["T=0"]]) - self.put(es, self.samplenum, self.out_ann, [9, ["APDU", "APDU cls={cls:02x} ins={ins:02x}".format(cls=bClass,ins=bIns), "APDU cls={cls:02x} ins={ins:02x} p1={p1:02x} p2={p2:02x} p3={p3:02x} len={len} status={sw1:02x}{sw2:02x}".format(cls=bClass,ins=bIns,p1=packet[2],p2=packet[3],p3=packet[4],len=dataLen,sw1=packet[-2],sw2=packet[-1])]]) - elif (self.hasT1): - isIBlock,packet = self.t1_parse_block(es) - if (isIBlock): - while (True): - isIBlock,packet2 = self.t1_parse_block(es) - if (isIBlock): - packet = packet + packet2 - break - self.put(es, self.samplenum, self.out_ann, [4, ["T=1", "T=1 (reassembled)"]]) - if (len(packet) >= 8): - self.put(es, self.samplenum, self.out_ann, [9, ["APDU", "APDU cls={cls:02x} ins={ins:02x}".format(cls=packet[0],ins=packet[1]), "APDU cls={cls:02x} ins={ins:02x} p1={p1:02x} p2={p2:02x} p3={p3:02x} len={len} status={sw1:02x}{sw2:02x}".format(cls=packet[0],ins=packet[1],p1=packet[2],p2=packet[3],p3=packet[4],len=len(packet) - 7,sw1=packet[-2],sw2=packet[-1])]]) - - - if (len(packet) > 0): - ts = self.ts_from_samplenum(es) - pkt = pcap_udp_pkt(ts, packet) - self.put(es, self.samplenum, self.out_binary, [0, pkt.record_header()]) - self.put(es, self.samplenum, self.out_binary, [0, pkt.packet()]) - - self.log("PACKETEND", codecs.encode(bytes(packet), 'hex')) - else: - break; diff --git a/Software/decoders/spiflash/pd.py b/Software/decoders/spiflash/pd.py index 26f3a24c..c9c3edf4 100644 --- a/Software/decoders/spiflash/pd.py +++ b/Software/decoders/spiflash/pd.py @@ -24,7 +24,7 @@ L = len(cmds) -a = [re.sub('\/', '_', c[0]).replace('2READ', 'READ2X') for c in cmds.values()] + ['BIT', 'FIELD', 'WARN'] +a = [re.sub(r'\/', '_', c[0]).replace('2READ', 'READ2X') for c in cmds.values()] + ['BIT', 'FIELD', 'WARN'] Ann = SrdIntEnum.from_list('Ann', a) def cmd_annotation_classes(): diff --git a/Software/decoders/usb_packet/pd.py b/Software/decoders/usb_packet/pd.py index e262074e..769db9a3 100644 --- a/Software/decoders/usb_packet/pd.py +++ b/Software/decoders/usb_packet/pd.py @@ -342,6 +342,10 @@ def handle_packet(self): self.packet_summary += ' %02X' % db self.packet_summary += ' ]' + if len(packet) < 32: + self.putp([28, ['Invalid packet (shorter than 32 bits)']]) + return + # Convenience Python output (no annotation) for all bytes together. self.ss, self.es = self.bits[16][1], self.bits[-16][2] self.putpb(['DATABYTES', databytes]) diff --git a/Software/decoders/usb_power_delivery/pd.py b/Software/decoders/usb_power_delivery/pd.py index 87ed5546..3788924a 100644 --- a/Software/decoders/usb_power_delivery/pd.py +++ b/Software/decoders/usb_power_delivery/pd.py @@ -418,7 +418,10 @@ def head_rev(self): return ((self.head >> 6) & 3) + 1 def head_type(self): - return self.head & 0xF + if self.head_rev() == 3: + return self.head & 0x1F + else: + return self.head & 0xF def head_count(self): return (self.head >> 12) & 7