├── vc2017 ├── Resource.rc ├── version.h ├── resource.h ├── SpiFlash.sln ├── SpiFlash.vcxproj.filters └── SpiFlash.vcxproj ├── prebuilt ├── SpiFlashSetup_1.0.3.0.msi ├── win32 │ └── SpiFlashAnalyzer.dll ├── win64 │ └── SpiFlashAnalyzer.dll └── SpiFlashSetup_1.0.3.0.x64.msi ├── LICENSE ├── README.md ├── vc2015 ├── SpiFlash.sln ├── SpiFlash.vcxproj.filters └── SpiFlash.vcxproj ├── rename_analyzer.py ├── source ├── SpiFlashAnalyzerResults.h ├── SpiFlashSimulationDataGenerator.h ├── SpiFlashAnalyzerSettings.h ├── SpiFlashAnalyzer.h ├── SpiFlashSimulationDataGenerator.cpp ├── SpiFlashAnalyzerSettings.cpp ├── SpiFlashAnalyzerResults.cpp ├── SpiFlash.h ├── SpiFlashAnalyzer.cpp └── SpiFlash.cpp └── .gitignore /vc2017/Resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kasjer/saleae_spiflash/HEAD/vc2017/Resource.rc -------------------------------------------------------------------------------- /prebuilt/SpiFlashSetup_1.0.3.0.msi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kasjer/saleae_spiflash/HEAD/prebuilt/SpiFlashSetup_1.0.3.0.msi -------------------------------------------------------------------------------- /prebuilt/win32/SpiFlashAnalyzer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kasjer/saleae_spiflash/HEAD/prebuilt/win32/SpiFlashAnalyzer.dll -------------------------------------------------------------------------------- /prebuilt/win64/SpiFlashAnalyzer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kasjer/saleae_spiflash/HEAD/prebuilt/win64/SpiFlashAnalyzer.dll -------------------------------------------------------------------------------- /prebuilt/SpiFlashSetup_1.0.3.0.x64.msi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kasjer/saleae_spiflash/HEAD/prebuilt/SpiFlashSetup_1.0.3.0.x64.msi -------------------------------------------------------------------------------- /vc2017/version.h: -------------------------------------------------------------------------------- 1 | #ifndef VERSION_H 2 | #define VERSION_H 3 | 4 | #ifndef VERSION_MAJOR 5 | #define VERSION_MAJOR 1 6 | #endif 7 | #ifndef VERSION_MINOR 8 | #define VERSION_MINOR 0 9 | #endif 10 | #ifndef VERSION_3 11 | #define VERSION_3 3 12 | #endif 13 | #ifndef VERSION_4 14 | #define VERSION_4 0 15 | #endif 16 | 17 | #ifndef VERSION_STRING 18 | #define VERSION_STRING "1.0.3.0" 19 | #endif 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /vc2017/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Resource.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 kasjer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPI flash protocol analyzer for Saleae 2 | 3 | Protocol analyzer for SPI flash decodes single, dual and quad commands. 4 | Several manufacturer specific command sets can be used. 5 | Most flashes can work in SPI mode 0 and 3, analyzer detects mode automatically or it can be set manually. 6 | 7 | # Features 8 | - SPI0 and SPI3 mode 9 | - Single, dual and quad mode 10 | - Commands for changing between single and quad or dual mode detected 11 | - Continues read mode detected 12 | - Register bit fields decoded 13 | - Following manufacturers command sets supported: 14 | - Winbond 15 | - Macronix 16 | - GigaDevice 17 | - Adesto 18 | - Microchip 19 | - Micron 20 | - Cypress 21 | - Issi 22 | 23 | # Installation 24 | 25 | For Windows there are two msi files in prebuilt folder, that will add analyzer to the existing Saleae Logic installation folder. 26 | 27 | Manual installation 28 | Simply put SpiFlashAnalyzer.dll in the Saleae *Analyzers* folder (typically: C:\Program Files\Saleae LLC\Analyzers). 29 | 30 | For Linux and Mac OSX library needs to be build with Saleae provided build_analyzer.py script. The library then can be copied to *Analyzer* folder in the Logic installation folder. 31 | -------------------------------------------------------------------------------- /vc2015/SpiFlash.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpiFlash", "SpiFlash.vcxproj", "{B396B091-5789-4294-A4EE-956A3E8CD46E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x64.ActiveCfg = Debug|x64 17 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x64.Build.0 = Debug|x64 18 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x86.Build.0 = Debug|Win32 20 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x64.ActiveCfg = Release|x64 21 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x64.Build.0 = Release|x64 22 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x86.ActiveCfg = Release|Win32 23 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /vc2017/SpiFlash.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpiFlashAnalyzer", "SpiFlash.vcxproj", "{B396B091-5789-4294-A4EE-956A3E8CD46E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x64.ActiveCfg = Debug|x64 17 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x64.Build.0 = Debug|x64 18 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Debug|x86.Build.0 = Debug|Win32 20 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x64.ActiveCfg = Release|x64 21 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x64.Build.0 = Release|x64 22 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x86.ActiveCfg = Release|Win32 23 | {B396B091-5789-4294-A4EE-956A3E8CD46E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /vc2015/SpiFlash.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | -------------------------------------------------------------------------------- /rename_analyzer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | 4 | # Fix Python 2.x. 5 | try: 6 | input = raw_input 7 | except NameError: pass 8 | 9 | print("") 10 | print("") 11 | print("What would you like to call your new analyzer?") 12 | print("") 13 | print(">>The files under '/source' will be modified to use it.") 14 | print(">>Examples include Serial, MySerial, JoesSerial, Gamecube, Wiimote, 2Wire, etc.") 15 | print(">>Do not inclide the trailing word 'Analyzer' this will be added automaticly.") 16 | print("") 17 | print("(press CTRL-C to cancel)") 18 | print("") 19 | 20 | new_analyzer_name = input( "Your new analyzer name: " ) 21 | 22 | print("") 23 | print("") 24 | print("What is the analyzer's title? (as shown in the add new anlayzer drop down)") 25 | print("") 26 | print(">>Examples include Async Serial, I2C, Joe's Serial, Gamecube, Wiimote, 2Wire, etc.") 27 | print("") 28 | print("(press CTRL-C to cancel)") 29 | print("") 30 | 31 | new_analyzer_title = input( "Your new analyzer's title: " ) 32 | 33 | source_path = "source" 34 | os.chdir( source_path ) 35 | 36 | files = dict() 37 | 38 | original_name = "SimpleSerial" 39 | 40 | cpp_files = glob.glob( "*.cpp" ); 41 | h_files = glob.glob( "*.h" ); 42 | 43 | for file in cpp_files: 44 | files[file] = ".cpp" 45 | 46 | for file in h_files: 47 | files[ file ] = ".h" 48 | 49 | new_files = [] 50 | 51 | for file, extension in files.items(): 52 | name_root = file.replace( original_name, "" ) 53 | new_name = new_analyzer_name + name_root 54 | #print "renaming file " + file + " to " + new_name 55 | os.rename( file, new_name ) 56 | new_files.append( new_name ) 57 | 58 | for file in new_files: 59 | contents = open( file, 'r' ).read() 60 | contents = contents.replace( original_name + "Analyzer", new_analyzer_name + "Analyzer" ) 61 | contents = contents.replace( original_name.upper() + "_ANALYZER_", new_analyzer_name.upper() + "_ANALYZER_" ) 62 | contents = contents.replace( original_name.upper() + "_SIMULATION_DATA_GENERATOR", new_analyzer_name.upper() + "_SIMULATION_DATA_GENERATOR" ) 63 | contents = contents.replace( original_name + "SimulationDataGenerator", new_analyzer_name + "SimulationDataGenerator" ) 64 | contents = contents.replace( "Simple Serial", new_analyzer_title ) 65 | open( file, 'w' ).write( contents ) 66 | 67 | -------------------------------------------------------------------------------- /vc2017/SpiFlash.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | 58 | 59 | Resource Files 60 | 61 | 62 | -------------------------------------------------------------------------------- /source/SpiFlashAnalyzerResults.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #ifndef SPIFLASH_ANALYZER_RESULTS 25 | #define SPIFLASH_ANALYZER_RESULTS 26 | 27 | #include 28 | 29 | enum FrameType 30 | { 31 | FT_OUT_BYTE, 32 | FT_OUT_ADDR24, 33 | FT_OUT_ADDR32, 34 | FT_IN_BYTE, 35 | FT_CMD, 36 | FT_CMD_BYTE, 37 | FT_DUMMY, 38 | FT_IN_OUT, 39 | FT_M, 40 | FT_IN_REG, 41 | FT_OUT_REG, 42 | }; 43 | 44 | class SpiFlashAnalyzer; 45 | class SpiFlashAnalyzerSettings; 46 | class RegisterData; 47 | 48 | class SpiFlashAnalyzerResults : public AnalyzerResults 49 | { 50 | void AddResult(const std::string &str) { AddResultString(str.c_str()); } 51 | void AddRegisterResult(RegisterData *reg, U64 val, DisplayBase display_base); 52 | public: 53 | SpiFlashAnalyzerResults( SpiFlashAnalyzer* analyzer, SpiFlashAnalyzerSettings* settings ); 54 | virtual ~SpiFlashAnalyzerResults(); 55 | 56 | virtual void GenerateBubbleText( U64 frame_index, Channel& channel, DisplayBase display_base ); 57 | virtual void GenerateExportFile( const char* file, DisplayBase display_base, U32 export_type_user_id ); 58 | 59 | virtual void GenerateFrameTabularText(U64 frame_index, DisplayBase display_base ); 60 | virtual void GeneratePacketTabularText( U64 packet_id, DisplayBase display_base ); 61 | virtual void GenerateTransactionTabularText( U64 transaction_id, DisplayBase display_base ); 62 | 63 | protected: //functions 64 | 65 | protected: //vars 66 | SpiFlashAnalyzerSettings* mSettings; 67 | SpiFlashAnalyzer* mAnalyzer; 68 | }; 69 | 70 | #endif //SPIFLASH_ANALYZER_RESULTS 71 | -------------------------------------------------------------------------------- /source/SpiFlashSimulationDataGenerator.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #ifndef SPIFLASH_SIMULATION_DATA_GENERATOR 25 | #define SPIFLASH_SIMULATION_DATA_GENERATOR 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | class SpiFlashAnalyzerSettings; 32 | 33 | class SpiFlashSimulationDataGenerator 34 | { 35 | // Bit sequence generated for command 36 | std::vector mPendingBits; 37 | // Current index in mPendingBits sequence 38 | size_t mPendingBitsIx; 39 | 40 | void GenerateNext(); 41 | void setBit(SimulationChannelDescriptor *channel, U8 high) 42 | { 43 | if (channel) 44 | channel->TransitionIfNeeded(high ? BIT_HIGH : BIT_LOW); 45 | } 46 | public: 47 | SpiFlashSimulationDataGenerator(); 48 | ~SpiFlashSimulationDataGenerator(); 49 | 50 | void Initialize(U32 simulation_sample_rate, SpiFlashAnalyzerSettings* settings); 51 | U32 GenerateSimulationData(U64 newest_sample_requested, U32 sample_rate, SimulationChannelDescriptor** simulation_channel); 52 | 53 | protected: 54 | ClockGenerator mClockGenerator; 55 | SpiFlashAnalyzerSettings* mSettings; 56 | U32 mSimulationSampleRateHz; 57 | 58 | protected: 59 | SimulationChannelDescriptorGroup mSimulationChannels; 60 | 61 | SimulationChannelDescriptor *mChipSelectSimulationData; 62 | SimulationChannelDescriptor *mClockSimulationData; 63 | SimulationChannelDescriptor *mMosiSimulationData; 64 | SimulationChannelDescriptor *mMisoSimulationData; 65 | SimulationChannelDescriptor *mD2SimulationData; 66 | SimulationChannelDescriptor *mD3SimulationData; 67 | }; 68 | #endif //SPIFLASH_SIMULATION_DATA_GENERATOR 69 | -------------------------------------------------------------------------------- /source/SpiFlashAnalyzerSettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #ifndef SPIFLASH_ANALYZER_SETTINGS 25 | #define SPIFLASH_ANALYZER_SETTINGS 26 | 27 | #include 28 | #include 29 | 30 | class SpiFlashAnalyzerSettings : public AnalyzerSettings 31 | { 32 | public: 33 | SpiFlashAnalyzerSettings(); 34 | virtual ~SpiFlashAnalyzerSettings(); 35 | 36 | virtual bool SetSettingsFromInterfaces(); 37 | void UpdateInterfacesFromSettings(); 38 | virtual void LoadSettings( const char* settings ); 39 | virtual const char* SaveSettings(); 40 | 41 | 42 | Channel mChipSelect; 43 | Channel mClock; 44 | Channel mMosi; 45 | Channel mMiso; 46 | Channel mD2; 47 | Channel mD3; 48 | U32 mManufacturer; 49 | U32 mAddressLength; 50 | U32 mSpiMode; 51 | U32 mBusMode; 52 | U32 mContinuousRead; 53 | 54 | protected: 55 | std::auto_ptr mManufacturerInterface; 56 | std::auto_ptr mAddressLengthInterface; 57 | std::auto_ptr mSpiModeInterface; 58 | std::auto_ptr mBusModeInterface; 59 | std::auto_ptr mContinuousReadInterface; 60 | 61 | std::auto_ptr mChipSelectInterface; 62 | std::auto_ptr mClockInterface; 63 | std::auto_ptr mMosiInterface; 64 | std::auto_ptr mMisoInterface; 65 | std::auto_ptr mD2Interface; 66 | std::auto_ptr mD3Interface; 67 | }; 68 | 69 | #endif //SPIFLASH_ANALYZER_SETTINGS 70 | -------------------------------------------------------------------------------- /source/SpiFlashAnalyzer.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #ifndef SPIFLASH_ANALYZER_H 25 | #define SPIFLASH_ANALYZER_H 26 | 27 | #include 28 | #include "SpiFlashAnalyzerResults.h" 29 | #include "SpiFlashSimulationDataGenerator.h" 30 | 31 | #include "SpiFlash.h" 32 | 33 | class SpiFlashAnalyzerSettings; 34 | class ANALYZER_EXPORT SpiFlashAnalyzer : public Analyzer2 35 | { 36 | public: 37 | SpiFlashAnalyzer(); 38 | virtual ~SpiFlashAnalyzer(); 39 | virtual void WorkerThread(); 40 | 41 | virtual U32 GenerateSimulationData(U64 newest_sample_requested, U32 sample_rate, SimulationChannelDescriptor** simulation_channels); 42 | virtual U32 GetMinimumSampleRateHz(); 43 | 44 | virtual const char *GetAnalyzerName() const; 45 | virtual bool NeedsRerun(); 46 | 47 | AnalyzerChannelData *GetAnalyzerChannelData(Channel& channel); 48 | protected: //vars 49 | std::auto_ptr mSettings; 50 | std::auto_ptr mResults; 51 | AnalyzerChannelData *mSerial; 52 | 53 | SpiFlashSimulationDataGenerator mSimulationDataGenerator; 54 | bool mSimulationInitilized; 55 | 56 | AnalyzerChannelData *mChipSelect; 57 | AnalyzerChannelData *mClock; 58 | AnalyzerChannelData *mMosi; 59 | AnalyzerChannelData *mMiso; 60 | AnalyzerChannelData *mD2; 61 | AnalyzerChannelData *mD3; 62 | 63 | //Serial analysis vars: 64 | U32 mSampleRateHz; 65 | U32 mStartOfStopBitOffset; 66 | U32 mEndOfStopBitOffset; 67 | BusMode mCurrentBusMode; 68 | BusMode mDefaultBusMode; 69 | bool mDirIn; 70 | 71 | // Starting sample, CS activated 72 | U64 mCommandStart; 73 | // Ending sample, CS deactivated 74 | U64 mCommandEnd; 75 | BitState mClockIdleState; 76 | // Continues read mode active after CS is activated 77 | SpiCmdData *mLockedCmd; 78 | private: 79 | void AddFrame(U64 start, U64 end, U64 d1, U64 d2, U8 type, U8 flags); 80 | void Setup(); 81 | void AdvanceToCommandStart(); 82 | void AdvanceDataToAbsPosition(U64 AbsolutePosition); 83 | void SetupResults(); 84 | void AnalyzeCommandBits(); 85 | void UpdateBusMode(BusMode busMode) { if (busMode) mCurrentBusMode = busMode; } 86 | U8 GetBits(BusMode busMode, bool dirIn); 87 | int ExtractBits(U64 &start, U64 &end, U32 &val, U8 bitCount); 88 | int ExtractMosiMiso(U64 &start, U64 &end, U8 &mosi, U8 &miso); 89 | 90 | void CacheClock(int num, U64 limit = 0); 91 | void CacheDropOlderClocks(U64 limit); 92 | 93 | U64 mCachedClocks[64]; 94 | U8 mCachedClockCount; 95 | 96 | }; 97 | 98 | extern "C" ANALYZER_EXPORT const char* __cdecl GetAnalyzerName(); 99 | extern "C" ANALYZER_EXPORT Analyzer* __cdecl CreateAnalyzer(); 100 | extern "C" ANALYZER_EXPORT void __cdecl DestroyAnalyzer(Analyzer* analyzer); 101 | 102 | #endif //SPIFLASH_ANALYZER_H 103 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /source/SpiFlashSimulationDataGenerator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "SpiFlashSimulationDataGenerator.h" 31 | #include "SpiFlashAnalyzerSettings.h" 32 | 33 | #include 34 | 35 | #include "SpiFlash.h" 36 | 37 | SpiFlashSimulationDataGenerator::SpiFlashSimulationDataGenerator() 38 | { 39 | } 40 | 41 | SpiFlashSimulationDataGenerator::~SpiFlashSimulationDataGenerator() 42 | { 43 | } 44 | 45 | void SpiFlashSimulationDataGenerator::Initialize(U32 simulation_sample_rate, SpiFlashAnalyzerSettings* settings) 46 | { 47 | double target_frequency = simulation_sample_rate / 10; 48 | mSimulationSampleRateHz = simulation_sample_rate; 49 | mSettings = settings; 50 | if (target_frequency > 104000000) 51 | target_frequency = 104000000; 52 | mClockGenerator.Init(target_frequency, simulation_sample_rate); 53 | spiFlash.SetSpiMode(mSettings->mSpiMode == 3 ? SPI_MODE3 : SPI_MODE0); 54 | spiFlash.SetCurrentCommand(nullptr); 55 | spiFlash.SetCurrentBusMode(BusMode(mSettings->mBusMode)); 56 | spiFlash.SetDefaultBusMode(BusMode(mSettings->mBusMode)); 57 | 58 | if (settings->mChipSelect.mChannelIndex < 1000) 59 | mChipSelectSimulationData = mSimulationChannels.Add(settings->mChipSelect, mSimulationSampleRateHz, BIT_HIGH); 60 | else 61 | mChipSelectSimulationData = nullptr; 62 | 63 | mClockSimulationData = mSimulationChannels.Add(settings->mClock, mSimulationSampleRateHz, 64 | (settings->mSpiMode == 3) ? BIT_HIGH : BIT_LOW); 65 | mMosiSimulationData = mSimulationChannels.Add(settings->mMosi, mSimulationSampleRateHz, BIT_HIGH); 66 | 67 | if (settings->mMiso.mChannelIndex < 1000) 68 | mMisoSimulationData = mSimulationChannels.Add(settings->mMiso, mSimulationSampleRateHz, BIT_HIGH); 69 | else 70 | mMisoSimulationData = nullptr; 71 | 72 | if (settings->mD2.mChannelIndex < 1000) 73 | mD2SimulationData = mSimulationChannels.Add(settings->mD2, mSimulationSampleRateHz, BIT_HIGH); 74 | else 75 | mD2SimulationData = nullptr; 76 | 77 | if (settings->mD3.mChannelIndex < 1000) 78 | mD3SimulationData = mSimulationChannels.Add(settings->mD3, mSimulationSampleRateHz, BIT_HIGH); 79 | else 80 | mD3SimulationData = nullptr; 81 | 82 | mSimulationChannels.AdvanceAll(mClockGenerator.AdvanceByHalfPeriod(100)); 83 | } 84 | 85 | U32 SpiFlashSimulationDataGenerator::GenerateSimulationData(U64 largest_sample_requested, U32 sample_rate, SimulationChannelDescriptor** simulation_channel) 86 | { 87 | U64 adjusted_largest_sample_requested = AnalyzerHelpers::AdjustSimulationTargetSample(largest_sample_requested, sample_rate, mSimulationSampleRateHz); 88 | 89 | while (mClockSimulationData->GetCurrentSampleNumber() < adjusted_largest_sample_requested) 90 | { 91 | GenerateNext(); 92 | } 93 | 94 | *simulation_channel = mSimulationChannels.GetArray(); 95 | return mSimulationChannels.GetCount(); 96 | } 97 | 98 | void SpiFlashSimulationDataGenerator::GenerateNext() 99 | { 100 | BusMode mode = spiFlash.GetCurrentBusMode(); 101 | U8 b; 102 | U8 IdleClockState = spiFlash.IdleClockState(); 103 | 104 | if (mPendingBitsIx >= mPendingBits.size()) 105 | { 106 | // Add some random delay before new command 107 | mSimulationChannels.AdvanceAll(mClockGenerator.AdvanceByHalfPeriod(10 + rand() % 10)); 108 | 109 | mPendingBits.clear(); 110 | // Set clock in neutral state before CS is activated 111 | mPendingBits.push_back(CS_HIGH | IdleClockState); 112 | // Activate CS 113 | mPendingBits.push_back(CS_LOW | IdleClockState); 114 | // Add some delay 115 | mPendingBits.push_back(spiFlash.Delay(1)); 116 | // Generate command bits 117 | spiFlash.GenerateRandomCommandBits(mPendingBits); 118 | // Make sure clock goes to idle state 119 | mPendingBits.push_back(CS_LOW | IdleClockState); 120 | // Deactivate CS 121 | mPendingBits.push_back(CS_HIGH | IdleClockState); 122 | mPendingBitsIx = 0; 123 | } 124 | 125 | // Get next bits or delay 126 | b = mPendingBits[mPendingBitsIx++]; 127 | if (b & HALF_CLOCK_DELAY) 128 | mSimulationChannels.AdvanceAll(mClockGenerator.AdvanceByHalfPeriod(b & 0x7F)); 129 | else 130 | { 131 | // Set all bits and move half period forward 132 | setBit(mChipSelectSimulationData, b & CS_HIGH); 133 | setBit(mClockSimulationData, b & CLOCK_HIGH); 134 | setBit(mMosiSimulationData, b & MOSI_HIGH); 135 | setBit(mMisoSimulationData, b & MISO_HIGH); 136 | setBit(mD2SimulationData, b & D2_HIGH); 137 | setBit(mD3SimulationData, b & D3_HIGH); 138 | mSimulationChannels.AdvanceAll(mClockGenerator.AdvanceByHalfPeriod(1)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /source/SpiFlashAnalyzerSettings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include "SpiFlashAnalyzerSettings.h" 25 | #include 26 | #include "SpiFlash.h" 27 | 28 | SpiFlashAnalyzerSettings::SpiFlashAnalyzerSettings() : 29 | mChipSelect(UNDEFINED_CHANNEL), 30 | mClock(UNDEFINED_CHANNEL), 31 | mMosi(UNDEFINED_CHANNEL), 32 | mMiso(UNDEFINED_CHANNEL), 33 | mD2(UNDEFINED_CHANNEL), 34 | mD3(UNDEFINED_CHANNEL), 35 | mManufacturer(0), 36 | mAddressLength(24), 37 | mSpiMode(0xFF), 38 | mBusMode(1) 39 | { 40 | mChipSelectInterface.reset(new AnalyzerSettingInterfaceChannel()); 41 | mChipSelectInterface->SetTitleAndTooltip("CS", "Select Chip select line"); 42 | mChipSelectInterface->SetChannel(mChipSelect); 43 | 44 | mClockInterface.reset(new AnalyzerSettingInterfaceChannel()); 45 | mClockInterface->SetTitleAndTooltip("Clock", "Select Clock line"); 46 | mClockInterface->SetChannel(mClock); 47 | 48 | mMosiInterface.reset(new AnalyzerSettingInterfaceChannel()); 49 | mMosiInterface->SetTitleAndTooltip("MOSI", "Select MOSI line"); 50 | mMosiInterface->SetChannel(mMosi); 51 | 52 | mMisoInterface.reset(new AnalyzerSettingInterfaceChannel()); 53 | mMisoInterface->SetTitleAndTooltip("MISO", "Select MISO line"); 54 | mMisoInterface->SetChannel(mMiso); 55 | 56 | mD2Interface.reset(new AnalyzerSettingInterfaceChannel()); 57 | mD2Interface->SetTitleAndTooltip("D2", "Select D2 line"); 58 | mD2Interface->SetSelectionOfNoneIsAllowed(true); 59 | mD2Interface->SetChannel(mD2); 60 | 61 | mD3Interface.reset(new AnalyzerSettingInterfaceChannel()); 62 | mD3Interface->SetTitleAndTooltip("D3", "Select D3 line"); 63 | mD3Interface->SetSelectionOfNoneIsAllowed(true); 64 | mD3Interface->SetChannel(mD3); 65 | 66 | mManufacturerInterface.reset(new AnalyzerSettingInterfaceNumberList()); 67 | mManufacturerInterface->SetTitleAndTooltip("Manufacturer", "Select flash manufacturer"); 68 | for (size_t i = 0; i < spiFlash.getCommandSets().size(); ++i) 69 | mManufacturerInterface->AddNumber(spiFlash.getCommandSets()[i]->GetId(), 70 | spiFlash.getCommandSets()[i]->GetName().c_str(), ""); 71 | 72 | mManufacturerInterface->SetNumber(mManufacturer); 73 | spiFlash.SelectCmdSet(mManufacturer); 74 | 75 | mAddressLengthInterface.reset(new AnalyzerSettingInterfaceNumberList()); 76 | mAddressLengthInterface->SetTitleAndTooltip("Address", "Select address length"); 77 | mAddressLengthInterface->AddNumber(24, "24 bits", ""); 78 | // TODO: Restore 32 bits 79 | //mAddressLengthInterface->AddNumber(32, "32 bits", ""); 80 | mAddressLengthInterface->SetNumber(mAddressLength); 81 | 82 | mSpiModeInterface.reset(new AnalyzerSettingInterfaceNumberList()); 83 | mSpiModeInterface->SetTitleAndTooltip("SPI Mode", "Select SPI mode"); 84 | mSpiModeInterface->AddNumber(0xFF, "Auto", ""); 85 | mSpiModeInterface->AddNumber(0, "SPI Mode 0", ""); 86 | mSpiModeInterface->AddNumber(3, "SPI Mode 3", ""); 87 | mSpiModeInterface->SetNumber(mSpiMode); 88 | 89 | mBusModeInterface.reset(new AnalyzerSettingInterfaceNumberList()); 90 | mBusModeInterface->SetTitleAndTooltip("Start in", "Number of lines used for command transmission"); 91 | mBusModeInterface->AddNumber(1, "Single", ""); 92 | mBusModeInterface->AddNumber(2, "Dual", ""); 93 | mBusModeInterface->AddNumber(4, "Quad", ""); 94 | mBusModeInterface->SetNumber(mBusMode); 95 | 96 | mContinuousReadInterface.reset(new AnalyzerSettingInterfaceNumberList()); 97 | mContinuousReadInterface->SetTitleAndTooltip("Continous read", "Command that is continuous read at start of ananlyzes"); 98 | mContinuousReadInterface->AddNumber(0, "command mode", ""); 99 | for (size_t i = 0; i < spiFlash.getCommandSets().size(); ++i) 100 | { 101 | CmdSet *cmdSet = spiFlash.getCommandSets()[i]; 102 | std::vector continueousReadCmds; 103 | cmdSet->GetContinousReadCommands(continueousReadCmds); 104 | 105 | for (size_t j = 0; j < continueousReadCmds.size(); ++j) 106 | { 107 | char t[100]; 108 | const SpiCmdData *cmd = continueousReadCmds[j]; 109 | snprintf(t, 100, "%02XH %s (%s)", cmd->GetCode(), cmd->mNames.back().c_str(), cmdSet->GetName().c_str()); 110 | mContinuousReadInterface->AddNumber((cmdSet->GetId() << 8) + cmd->GetCode(), t, ""); 111 | } 112 | } 113 | mContinuousReadInterface->SetNumber(0); 114 | 115 | AddInterface(mChipSelectInterface.get()); 116 | AddInterface(mClockInterface.get()); 117 | AddInterface(mMosiInterface.get()); 118 | AddInterface(mMisoInterface.get()); 119 | AddInterface(mD2Interface.get()); 120 | AddInterface(mD3Interface.get()); 121 | AddInterface(mManufacturerInterface.get()); 122 | AddInterface(mAddressLengthInterface.get()); 123 | AddInterface(mSpiModeInterface.get()); 124 | AddInterface(mBusModeInterface.get()); 125 | AddInterface(mContinuousReadInterface.get()); 126 | 127 | AddExportOption(0, "Export as text/csv file"); 128 | AddExportExtension(0, "text", "txt"); 129 | AddExportExtension(0, "csv", "csv"); 130 | 131 | ClearChannels(); 132 | 133 | AddChannel(mChipSelect, "Chip Select", false); 134 | AddChannel(mClock, "Clock", false); 135 | AddChannel(mMosi, "MOSI", false); 136 | AddChannel(mMiso, "MISO", false); 137 | AddChannel(mD2, "D2", false); 138 | AddChannel(mD3, "D3", false); 139 | } 140 | 141 | SpiFlashAnalyzerSettings::~SpiFlashAnalyzerSettings() 142 | { 143 | } 144 | 145 | bool SpiFlashAnalyzerSettings::SetSettingsFromInterfaces() 146 | { 147 | mManufacturer = U32(mManufacturerInterface->GetNumber()); 148 | mAddressLength = U32(mAddressLengthInterface->GetNumber()); 149 | mSpiMode = U32(mSpiModeInterface->GetNumber()); 150 | mBusMode = U32(mBusModeInterface->GetNumber()); 151 | mContinuousRead = U32(mContinuousReadInterface->GetNumber()); 152 | mChipSelect = mChipSelectInterface->GetChannel(); 153 | mClock = mClockInterface->GetChannel(); 154 | mMosi = mMosiInterface->GetChannel(); 155 | mMiso = mMisoInterface->GetChannel(); 156 | mD2 = mD2Interface->GetChannel(); 157 | mD3 = mD3Interface->GetChannel(); 158 | 159 | ClearChannels(); 160 | 161 | AddChannel(mChipSelect, "Chip Select", true); 162 | AddChannel(mClock, "Clock", true); 163 | AddChannel(mMosi, "MOSI", true); 164 | AddChannel(mMiso, "MISO", true); 165 | AddChannel(mD2, "D2", true); 166 | AddChannel(mD3, "D3", true); 167 | 168 | spiFlash.SelectCmdSet(mManufacturer); 169 | 170 | return true; 171 | } 172 | 173 | void SpiFlashAnalyzerSettings::UpdateInterfacesFromSettings() 174 | { 175 | mManufacturerInterface->SetNumber(mManufacturer); 176 | mAddressLengthInterface->SetNumber(mAddressLength); 177 | mSpiModeInterface->SetNumber(mSpiMode); 178 | mBusModeInterface->SetNumber(mBusMode); 179 | mContinuousReadInterface->SetNumber(mContinuousRead); 180 | mChipSelectInterface->SetChannel(mChipSelect); 181 | mClockInterface->SetChannel(mClock); 182 | mMosiInterface->SetChannel(mMosi); 183 | mMisoInterface->SetChannel(mMiso); 184 | mD2Interface->SetChannel(mD2); 185 | mD3Interface->SetChannel(mD3); 186 | } 187 | 188 | void SpiFlashAnalyzerSettings::LoadSettings(const char* settings) 189 | { 190 | SimpleArchive text_archive; 191 | text_archive.SetString(settings); 192 | 193 | text_archive >> mManufacturer; 194 | text_archive >> mAddressLength; 195 | text_archive >> mSpiMode; 196 | text_archive >> mBusMode; 197 | text_archive >> mContinuousRead; 198 | text_archive >> mChipSelect; 199 | text_archive >> mClock; 200 | text_archive >> mMosi; 201 | text_archive >> mMiso; 202 | text_archive >> mD2; 203 | text_archive >> mD3; 204 | 205 | ClearChannels(); 206 | AddChannel(mChipSelect, "Chip Select", true); 207 | AddChannel(mClock, "Clock", true); 208 | AddChannel(mMosi, "MOSI", true); 209 | AddChannel(mMiso, "MISO", true); 210 | AddChannel(mD2, "D2", true); 211 | AddChannel(mD3, "D3", true); 212 | 213 | UpdateInterfacesFromSettings(); 214 | } 215 | 216 | const char* SpiFlashAnalyzerSettings::SaveSettings() 217 | { 218 | SimpleArchive text_archive; 219 | 220 | text_archive << mManufacturer; 221 | text_archive << mAddressLength; 222 | text_archive << mSpiMode; 223 | text_archive << mBusMode; 224 | text_archive << mContinuousRead; 225 | text_archive << mChipSelect; 226 | text_archive << mClock; 227 | text_archive << mMosi; 228 | text_archive << mMiso; 229 | text_archive << mD2; 230 | text_archive << mD3; 231 | 232 | return SetReturnString(text_archive.GetString()); 233 | } 234 | -------------------------------------------------------------------------------- /vc2015/SpiFlash.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {B396B091-5789-4294-A4EE-956A3E8CD46E} 23 | Win32Proj 24 | SpiFlash 25 | 8.1 26 | 27 | 28 | 29 | DynamicLibrary 30 | true 31 | v140 32 | Unicode 33 | Static 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v140 39 | true 40 | Unicode 41 | Static 42 | 43 | 44 | DynamicLibrary 45 | true 46 | v140 47 | Unicode 48 | Static 49 | 50 | 51 | DynamicLibrary 52 | false 53 | v140 54 | true 55 | Unicode 56 | Static 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | false 87 | 88 | 89 | 90 | 91 | 92 | Level3 93 | Disabled 94 | WIN32;_DEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 95 | $(SALEAESDK)/include 96 | 4251 97 | Async 98 | 99 | 100 | Windows 101 | true 102 | $(SALEAESDK)/lib 103 | Analyzer.lib 104 | 105 | 106 | 107 | 108 | 109 | 110 | Level3 111 | Disabled 112 | WIN32;_DEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 113 | $(SALEAESDK)/include 114 | 4251 115 | Async 116 | 117 | 118 | Windows 119 | true 120 | $(SALEAESDK)/lib 121 | Analyzer64.lib;User32.lib 122 | 123 | 124 | 125 | 126 | Level3 127 | 128 | 129 | MaxSpeed 130 | true 131 | true 132 | WIN32;NDEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 133 | $(SALEAESDK)/include 134 | 4251 135 | Async 136 | 137 | 138 | Windows 139 | true 140 | true 141 | true 142 | $(SALEAESDK)/lib 143 | Analyzer.lib 144 | 145 | 146 | 147 | 148 | Level3 149 | 150 | 151 | MaxSpeed 152 | true 153 | true 154 | WIN32;NDEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 155 | $(SALEAESDK)/include 156 | 4251 157 | Async 158 | 159 | 160 | Windows 161 | true 162 | true 163 | true 164 | $(SALEAESDK)/lib 165 | Analyzer64.lib 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /vc2017/SpiFlash.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {B396B091-5789-4294-A4EE-956A3E8CD46E} 23 | Win32Proj 24 | SpiFlash 25 | 10.0.16299.0 26 | SpiFlashAnalyzer 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v141 33 | Unicode 34 | Static 35 | 36 | 37 | DynamicLibrary 38 | false 39 | v141 40 | true 41 | Unicode 42 | Static 43 | 44 | 45 | DynamicLibrary 46 | true 47 | v141 48 | Unicode 49 | Static 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v141 55 | true 56 | Unicode 57 | Static 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | false 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 96 | $(SALEAESDK)/include 97 | 4251 98 | Async 99 | 100 | 101 | Windows 102 | true 103 | $(SALEAESDK)/lib 104 | Analyzer.lib 105 | 106 | 107 | 108 | 109 | 110 | 111 | Level3 112 | Disabled 113 | WIN32;_DEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 114 | $(SALEAESDK)/include 115 | 4251 116 | Async 117 | 118 | 119 | Windows 120 | true 121 | $(SALEAESDK)/lib 122 | Analyzer64.lib;User32.lib 123 | 124 | 125 | 126 | 127 | Level3 128 | 129 | 130 | MaxSpeed 131 | true 132 | true 133 | WIN32;NDEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 134 | $(SALEAESDK)/include 135 | 4251 136 | Async 137 | 138 | 139 | Windows 140 | true 141 | true 142 | true 143 | $(SALEAESDK)/lib 144 | Analyzer.lib 145 | 146 | 147 | 148 | 149 | Level3 150 | 151 | 152 | MaxSpeed 153 | true 154 | true 155 | WIN32;NDEBUG;_WINDOWS;_USRDLL;SPIFLASH_EXPORTS;%(PreprocessorDefinitions) 156 | $(SALEAESDK)/include 157 | 4251 158 | Async 159 | 160 | 161 | Windows 162 | true 163 | true 164 | true 165 | $(SALEAESDK)/lib 166 | Analyzer64.lib 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /source/SpiFlashAnalyzerResults.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include "SpiFlashAnalyzerResults.h" 25 | #include 26 | #include "SpiFlashAnalyzer.h" 27 | #include "SpiFlashAnalyzerSettings.h" 28 | #include 29 | #include 30 | #include 31 | 32 | #include "SpiFlash.h" 33 | 34 | SpiFlashAnalyzerResults::SpiFlashAnalyzerResults(SpiFlashAnalyzer* analyzer, SpiFlashAnalyzerSettings* settings) 35 | : AnalyzerResults(), 36 | mSettings(settings), 37 | mAnalyzer(analyzer) 38 | { 39 | } 40 | 41 | SpiFlashAnalyzerResults::~SpiFlashAnalyzerResults() 42 | { 43 | } 44 | 45 | static int AddressBits(U32 addr) 46 | { 47 | if (addr < 0x100) 48 | return 8; 49 | else if (addr < 0x10000) 50 | return 16; 51 | else if (addr < 0x1000000) 52 | return 24; 53 | else 54 | return 32; 55 | } 56 | 57 | static std::string RegisterString(RegisterData *reg, U64 val, bool full = false) 58 | { 59 | std::stringstream s; 60 | 61 | for (size_t i = 0; i < reg->GetBitfieldCount(); ++i) 62 | { 63 | const BitField &bitField = reg->at(i); 64 | U32 bitsValue = bitField.GetValue(val); 65 | if (bitsValue || full) 66 | s << (s.tellp() ? " " : "") << bitField.mFieldName << "=" << std::hex << bitsValue; 67 | } 68 | return s.str(); 69 | } 70 | 71 | void SpiFlashAnalyzerResults::AddRegisterResult(RegisterData *reg, U64 val, DisplayBase display_base) 72 | { 73 | char number_str[128]; 74 | AnalyzerHelpers::GetNumberString(val, display_base, 8, number_str, 128); 75 | AddResultString(number_str); 76 | // There is register assigned 77 | if (reg) 78 | { 79 | std::string s = RegisterString(reg, val); 80 | if (s.size()) 81 | AddResult(s); 82 | AddResult(RegisterString(reg, val, true)); 83 | } 84 | } 85 | 86 | void SpiFlashAnalyzerResults::GenerateBubbleText(U64 frame_index, Channel& channel, DisplayBase display_base) 87 | { 88 | ClearResultStrings(); 89 | Frame frame = GetFrame(frame_index); 90 | 91 | char number_str[128]; 92 | char number_str2[10]; 93 | std::stringstream fulls, shorts; 94 | 95 | if (frame.mType == FT_CMD && channel == mSettings->mChipSelect) 96 | { 97 | SpiCmdData *cmd = reinterpret_cast(frame.mData2); 98 | if (U64(cmd) > 0x100) 99 | { 100 | size_t i; 101 | const char *s[4] = { 0 }; 102 | for (i = 0; i < cmd->mNames.size(); ++i) 103 | AddResultString(cmd->mNames[i].c_str()); 104 | if (cmd->mAddressBits) 105 | { 106 | U32 addr = U32(frame.mData1 >> 24); 107 | s[0] = " A="; 108 | s[1] = number_str; 109 | AnalyzerHelpers::GetNumberString(addr, Hexadecimal, AddressBits(addr), number_str, 128); 110 | } 111 | if (cmd->mCmdOp == OP_DATA_READ || cmd->mCmdOp == OP_DATA_WRITE) 112 | { 113 | s[2] = " bytes:"; 114 | s[3] = number_str2; 115 | AnalyzerHelpers::GetNumberString(frame.mData1 & 0xFFFFFF, Decimal, 24, number_str2, 128); 116 | } 117 | // Add longest name with address and byte count if present 118 | AddResultString(cmd->mNames[i - 1].c_str(), s[0], s[1], s[2], s[3]); 119 | } 120 | else 121 | { 122 | AddResultString("??"); 123 | if (frame.mData2 != 0x100) 124 | { 125 | AnalyzerHelpers::GetNumberString(frame.mData2, display_base, 8, number_str, 128); 126 | AddResultString("?? CMD=", number_str); 127 | } 128 | } 129 | } 130 | else if (frame.mType == FT_CMD_BYTE && channel == mSettings->mMosi) 131 | { 132 | SpiCmdData *cmd = reinterpret_cast(frame.mData2); 133 | if (frame.mData2 == 0x100) 134 | AddResultString("?"); // Not enough bits 135 | else if (frame.mData2 < 0x100) 136 | { 137 | // Normal byte and CMD=0xXX 138 | AnalyzerHelpers::GetNumberString(frame.mData2, display_base, 8, number_str, 128); 139 | AddResultString(number_str); 140 | AnalyzerHelpers::GetNumberString(frame.mData2, Hexadecimal, 8, number_str, 128); 141 | AddResultString("CMD=", number_str); 142 | } 143 | else 144 | { 145 | size_t i; 146 | U8 b = cmd->GetCode(); 147 | AnalyzerHelpers::GetNumberString(b, display_base, 8, number_str, 128); 148 | AddResultString(number_str); 149 | AnalyzerHelpers::GetNumberString(b, Hexadecimal, 8, number_str, 128); 150 | AddResultString("CMD=", number_str); 151 | for (i = 0; i < cmd->mNames.size(); ++i) 152 | AddResultString(cmd->mNames[i].c_str()); 153 | AddResultString(cmd->mNames[i - 1].c_str(), " CMD=", number_str); 154 | } 155 | } 156 | else if (frame.mType == FT_OUT_ADDR24 && channel == mSettings->mMosi) 157 | { 158 | AnalyzerHelpers::GetNumberString(frame.mData1, Hexadecimal, AddressBits(U32(frame.mData1 >> 24)), 159 | number_str, 128); 160 | AddResultString("A"); 161 | AddResultString(number_str); 162 | AddResultString("A=", number_str); 163 | } 164 | else if ((frame.mType == FT_OUT_BYTE || frame.mType == FT_IN_OUT) && channel == mSettings->mMosi) 165 | { 166 | AnalyzerHelpers::GetNumberString(frame.mData1, display_base, 8, number_str, 128); 167 | AddResultString(number_str); 168 | } 169 | else if (frame.mType == FT_IN_REG && channel == mSettings->mMiso) 170 | { 171 | AddRegisterResult(reinterpret_cast(frame.mData1), frame.mData2, display_base); 172 | } 173 | else if (frame.mType == FT_OUT_REG && channel == mSettings->mMosi) 174 | { 175 | AddRegisterResult(reinterpret_cast(frame.mData2), frame.mData1, display_base); 176 | } 177 | else if ((frame.mType == FT_M) && channel == mSettings->mMosi) 178 | { 179 | AnalyzerHelpers::GetNumberString(frame.mData1, display_base, 8, number_str, 128); 180 | AddResultString("M"); 181 | AddResultString(number_str); 182 | AnalyzerHelpers::GetNumberString(frame.mData1, Hexadecimal, 8, number_str, 128); 183 | AddResultString("M=", number_str); 184 | } 185 | else if ((frame.mType == FT_IN_BYTE || frame.mType == FT_IN_OUT) && channel == mSettings->mMiso) 186 | { 187 | AnalyzerHelpers::GetNumberString(frame.mData2, display_base, 8, number_str, 128); 188 | AddResultString(number_str); 189 | } 190 | else if (frame.mType == FT_DUMMY && channel == mSettings->mMosi) 191 | { 192 | AddResultString("x"); 193 | AddResultString("Dummy"); 194 | } 195 | } 196 | 197 | void SpiFlashAnalyzerResults::GenerateExportFile(const char* file, DisplayBase display_base, U32 export_type_user_id) 198 | { 199 | std::ofstream file_stream(file, std::ios::out); 200 | 201 | U64 trigger_sample = mAnalyzer->GetTriggerSample(); 202 | U32 sample_rate = mAnalyzer->GetSampleRate(); 203 | 204 | file_stream << "Time [s],Value" << '\n'; 205 | 206 | U64 num_frames = GetNumFrames(); 207 | for (U32 i = 0; i < num_frames; i++) 208 | { 209 | Frame frame = GetFrame(i); 210 | 211 | char time_str[128]; 212 | AnalyzerHelpers::GetTimeString(frame.mStartingSampleInclusive, trigger_sample, sample_rate, time_str, 128); 213 | 214 | char number_str[128]; 215 | AnalyzerHelpers::GetNumberString(frame.mData1, display_base, 8, number_str, 128); 216 | 217 | file_stream << time_str << "," << number_str << '\n'; 218 | 219 | if (UpdateExportProgressAndCheckForCancel(i, num_frames) == true) 220 | { 221 | file_stream.close(); 222 | return; 223 | } 224 | } 225 | 226 | file_stream.close(); 227 | } 228 | 229 | void SpiFlashAnalyzerResults::GenerateFrameTabularText(U64 frame_index, DisplayBase display_base) 230 | { 231 | ClearTabularText(); 232 | Frame frame = GetFrame(frame_index); 233 | 234 | char number_str[128]; 235 | char number_str2[10]; 236 | if (frame.mType == FT_CMD) 237 | { 238 | SpiCmdData *cmd = reinterpret_cast(frame.mData2); 239 | if (U64(cmd) > 0x100) 240 | { 241 | const char *s[4] = { 0 }; 242 | if (cmd->mAddressBits) 243 | { 244 | U32 addr = U32(frame.mData1 >> 24); 245 | s[0] = " A="; 246 | s[1] = number_str; 247 | AnalyzerHelpers::GetNumberString(addr, Hexadecimal, AddressBits(addr), number_str, 128); 248 | } 249 | if (cmd->mCmdOp == OP_DATA_READ || cmd->mCmdOp == OP_DATA_WRITE) 250 | { 251 | s[2] = " bytes:"; 252 | s[3] = number_str2; 253 | AnalyzerHelpers::GetNumberString(frame.mData1 & 0xFFFFFF, Decimal, 24, number_str2, 128); 254 | } 255 | // Add longest name with address and byte count if present 256 | AddTabularText(cmd->mNames.back().c_str(), s[0], s[1], s[2], s[3]); 257 | } 258 | else 259 | { 260 | if (frame.mData2 != 0x100) 261 | { 262 | AnalyzerHelpers::GetNumberString(frame.mData2, display_base, 8, number_str, 128); 263 | AddTabularText("?? CMD=", number_str); 264 | } 265 | } 266 | } 267 | else if (frame.mType == FT_OUT_ADDR24) 268 | { 269 | AnalyzerHelpers::GetNumberString(frame.mData1, Hexadecimal, AddressBits(U32(frame.mData1 >> 24)), 270 | number_str, 128); 271 | AddTabularText("A=", number_str); 272 | } 273 | else if ((frame.mType == FT_OUT_BYTE || frame.mType == FT_IN_OUT)) 274 | { 275 | AnalyzerHelpers::GetNumberString(frame.mData1, display_base, 8, number_str, 128); 276 | AddTabularText(number_str); 277 | } 278 | else if ((frame.mType == FT_M)) 279 | { 280 | AnalyzerHelpers::GetNumberString(frame.mData1, Hexadecimal, 8, number_str, 128); 281 | AddTabularText("M=", number_str); 282 | } 283 | else if ((frame.mType == FT_IN_BYTE || frame.mType == FT_IN_OUT)) 284 | { 285 | AnalyzerHelpers::GetNumberString(frame.mData2, display_base, 8, number_str, 128); 286 | AddTabularText(number_str); 287 | } 288 | else if (frame.mType == FT_DUMMY) 289 | { 290 | AddTabularText("Dummy"); 291 | } 292 | } 293 | 294 | void SpiFlashAnalyzerResults::GeneratePacketTabularText(U64 packet_id, DisplayBase display_base) 295 | { 296 | ClearResultStrings(); 297 | AddResultString("not supported"); 298 | } 299 | 300 | void SpiFlashAnalyzerResults::GenerateTransactionTabularText(U64 transaction_id, DisplayBase display_base) 301 | { 302 | ClearResultStrings(); 303 | AddResultString("not supported"); 304 | } 305 | -------------------------------------------------------------------------------- /source/SpiFlash.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #ifndef SPIFLASH_H 25 | #define SPIFLASH_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | struct BitField 36 | { 37 | std::string mFieldName; 38 | U8 mUpperBit; 39 | U8 mLowerBit; 40 | BitField(U8 pos, const char *name) : mUpperBit(pos), mLowerBit(pos), mFieldName(name) {} 41 | BitField(U8 upper, U8 lower, const char *name) : mUpperBit(upper), mLowerBit(lower), mFieldName(name) {} 42 | U32 GetValue(U64 reg) const { return U32((reg & ((1 << (mUpperBit + 1)) - 1)) >> mLowerBit); } 43 | }; 44 | 45 | typedef BitField Bit; 46 | 47 | class RegisterData 48 | { 49 | std::vector mBits; 50 | std::string mName; 51 | U8 mLen; 52 | U64 mValue; 53 | public: 54 | RegisterData(const char *name, U8 len = 8) : mName(name), mLen(len) {} 55 | RegisterData(const RegisterData &o) : mBits(o.mBits), mName(o.mName), mLen(o.mLen) {} 56 | void SetLength(U8 len) { mLen = len; } 57 | const std::string GetName() const { return mName; } 58 | size_t GetBitfieldCount(void) { return mBits.size(); } 59 | const BitField &at(size_t ix) { return mBits.at(ix); } 60 | 61 | void AddBitField(const BitField &field) 62 | { 63 | mBits.push_back(field); 64 | } 65 | void AddBitRange(U8 n1, U8 n2, const char *name) 66 | { 67 | AddBitField(BitField(n1, n2, name)); 68 | } 69 | void AddBit(U8 n, const char *name) 70 | { 71 | AddBitRange(n, n, name); 72 | } 73 | bool operator==(const std::string name) 74 | { 75 | return mName.compare(name) == 0; 76 | } 77 | }; 78 | 79 | enum SpiMode 80 | { 81 | SPI_MODE0, 82 | SPI_MODE1, 83 | SPI_MODE2, 84 | SPI_MODE3 85 | }; 86 | 87 | enum BusMode 88 | { 89 | UNDEFINED = 0, 90 | SINGLE = 1, 91 | DUAL = 2, 92 | QUAD = 4, 93 | }; 94 | 95 | enum CmdMode 96 | { 97 | CM_1 = BusMode::SINGLE, 98 | CM_2 = BusMode::DUAL, 99 | CM_4 = BusMode::QUAD, 100 | CM_12 = (BusMode::SINGLE | BusMode::DUAL), 101 | CM_14 = (BusMode::SINGLE | BusMode::QUAD), 102 | CM_24 = (BusMode::DUAL | BusMode::QUAD), 103 | CM_124 = (BusMode::SINGLE | BusMode::DUAL | BusMode::QUAD), 104 | CM_ALL = CM_124, 105 | }; 106 | 107 | enum GeneratedData 108 | { 109 | CS_LOW = 0, 110 | CLOCK_LOW = 0, 111 | MOSI_LOW = 0, 112 | MISO_LOW = 0, 113 | D2_LOW = 0, 114 | D3_LOW = 0, 115 | CS_HIGH = 1, 116 | CLOCK_HIGH = 2, 117 | MOSI_HIGH = 4, 118 | MISO_HIGH = 8, 119 | D2_HIGH = 16, 120 | D3_HIGH = 32, 121 | HALF_CLOCK_DELAY = 128 122 | }; 123 | 124 | enum CmdFeature 125 | { 126 | ADDR, 127 | ADDR1, 128 | ADDR2, 129 | ADDR3, 130 | ADDR4, 131 | M, 132 | DUAL_IO, 133 | DUAL_DATA, 134 | QUAD_IO, 135 | QUAD_DATA, 136 | SET_SINGLE, 137 | SET_DUAL, 138 | SET_QUAD, 139 | }; 140 | 141 | enum CmdOp 142 | { 143 | OP_NO_DATA, 144 | OP_REG_READ, 145 | OP_REG_WRITE, 146 | OP_DATA_READ, 147 | OP_DATA_WRITE, 148 | }; 149 | 150 | struct DummyBytes 151 | { 152 | U8 mCnt; 153 | DummyBytes(U8 cnt) : mCnt(cnt) {} 154 | }; 155 | 156 | struct DummyCycles 157 | { 158 | U8 mCnt; 159 | DummyCycles(U8 cnt) : mCnt(cnt) {} 160 | }; 161 | 162 | struct RegisterOp 163 | { 164 | std::string mName; 165 | CmdOp mOp; 166 | RegisterOp(const char *name, CmdOp op) : mName(name), mOp(op) {} 167 | }; 168 | 169 | static inline RegisterData *Register(const char *name, U8 length) 170 | { 171 | return new RegisterData(name, length); 172 | } 173 | 174 | static inline RegisterOp RegisterRead(const char *name) 175 | { 176 | return RegisterOp(name, CmdOp::OP_REG_READ); 177 | } 178 | 179 | static inline RegisterOp RegisterWrite(const char *name) 180 | { 181 | return RegisterOp(name, CmdOp::OP_REG_WRITE); 182 | } 183 | 184 | class SpiCmdData 185 | { 186 | public: 187 | U8 mCode; 188 | CmdMode mMode; 189 | CmdOp mCmdOp; 190 | uint8_t mAddressBits; // 0 - command does not have address, 0xFF = default length 191 | bool mContinuousRead; 192 | bool mDummyBytes; 193 | bool mDummyCycles; 194 | U8 mDummyCount; 195 | U8 mModeChange; 196 | U8 mModeArgs; 197 | U8 mModeData; 198 | std::vector mNames; 199 | std::vector mRegs; 200 | public: 201 | SpiCmdData(U8 code, CmdMode mode, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) : mCode(code), mMode(mode), 202 | mCmdOp(OP_NO_DATA), mAddressBits(0), mDummyBytes(false), mDummyCycles(false), mContinuousRead(false), 203 | mModeChange(0), mModeArgs(0), mModeData(0) 204 | { 205 | mNames.push_back(std::string(n1)); 206 | if (n2) 207 | mNames.push_back(std::string(n2)); 208 | if (n3) 209 | mNames.push_back(std::string(n3)); 210 | } 211 | ~SpiCmdData() {} 212 | 213 | U8 GetCode() const { return mCode; } 214 | 215 | bool IsSingle() const { return (mMode & CmdMode::CM_1) != 0; } 216 | bool IsDual() const { return (mMode & CmdMode::CM_2) != 0; } 217 | bool IsQuad() const { return (mMode & CmdMode::CM_4) != 0; } 218 | bool IsValidForMode(BusMode mode) const { return (mMode & mode) != 0; } 219 | 220 | void AddName(const char *name) { mNames.push_back(name); } 221 | void AddReg(RegisterData *reg) { mRegs.push_back(reg); } 222 | RegisterData *GetRegister(size_t ix) { return mRegs.size() ? mRegs.at(ix % mRegs.size()) : nullptr; } 223 | size_t RegisterCount() const { return mRegs.size(); } 224 | 225 | void Set(CmdFeature feature) 226 | { 227 | switch (feature) 228 | { 229 | case ADDR: 230 | mAddressBits = 0xFF; 231 | break; 232 | case ADDR1: 233 | case ADDR2: 234 | case ADDR3: 235 | case ADDR4: 236 | mAddressBits = (feature - ADDR) * 8; 237 | break; 238 | case M: 239 | mContinuousRead = true; 240 | break; 241 | case DUAL_IO: 242 | mModeArgs = 2; 243 | break; 244 | case DUAL_DATA: 245 | mModeData = 2; 246 | break; 247 | case QUAD_IO: 248 | mModeArgs = 4; 249 | break; 250 | case QUAD_DATA: 251 | mModeData = 4; 252 | break; 253 | case SET_SINGLE: 254 | mModeChange = CM_1; 255 | break; 256 | case SET_DUAL: 257 | mModeChange = CM_2; 258 | break; 259 | case SET_QUAD: 260 | mModeChange = CM_4; 261 | break; 262 | default: 263 | break; 264 | } 265 | } 266 | void Set(CmdOp op) { mCmdOp = op; } 267 | void Set(const DummyBytes &db) { mDummyCount = db.mCnt; mDummyBytes = true; mDummyCycles = false; } 268 | void Set(const DummyCycles &db) { mDummyCount = db.mCnt; mDummyBytes = false; mDummyCycles = true; } 269 | }; 270 | 271 | class CmdSet 272 | { 273 | typedef std::map CommandMap; 274 | std::vector mRegisters; 275 | CommandMap mCommandMap; 276 | std::vector> mCommands; 277 | CmdSet *mParent; 278 | std::string mName; 279 | int mId; 280 | public: 281 | int GetId() const { return mId; } 282 | const std::string &GetName() const { return mName; } 283 | CmdSet(int id, const std::string name, CmdSet *parent = nullptr) : mId(id), mName(name), mParent(parent) {} 284 | void AddRegister(RegisterData *reg) { mRegisters.push_back(reg); } 285 | RegisterData *GetRegister(const char *name) 286 | { 287 | std::vector::iterator i; 288 | for (i = mRegisters.begin(); i != mRegisters.end(); ++i) 289 | if ((*i)->GetName().compare(name) == 0) 290 | break; 291 | 292 | if (i != mRegisters.end()) 293 | return *i; 294 | 295 | AddRegister(new RegisterData(name, 8)); 296 | return mRegisters.back(); 297 | } 298 | void AddRegisterField(const BitField &field) 299 | { 300 | if (mRegisters.size()) 301 | mRegisters.back()->AddBitField(field); 302 | } 303 | 304 | void AddCommand(SpiCmdData *cmd) 305 | { 306 | mCommands.push_back(std::auto_ptr(cmd)); 307 | if (cmd->IsSingle()) 308 | mCommandMap[(uint16_t)cmd->GetCode()] = cmd; 309 | if (cmd->IsDual()) 310 | mCommandMap[0x200 + cmd->GetCode()] = cmd; 311 | if (cmd->IsQuad()) 312 | mCommandMap[0x400 + cmd->GetCode()] = cmd; 313 | } 314 | void SetParent(CmdSet *parent) { mParent = parent; } 315 | void GetValidCommands(BusMode busMode, std::vector &cmds) const 316 | { 317 | // Get commands from parnet first 318 | if (mParent) 319 | mParent->GetValidCommands(busMode, cmds); 320 | 321 | // Add commands that are valid for specifed bus mode 322 | for (size_t i = 0; i < mCommands.size(); ++i) 323 | if (mCommands[i]->IsValidForMode(busMode)) 324 | cmds.push_back(mCommands[i]->GetCode()); 325 | 326 | // If parent existed, sort and remove duplicates 327 | if (mParent) 328 | { 329 | std::sort(cmds.begin(), cmds.end()); 330 | std::unique(cmds.begin(), cmds.end()); 331 | } 332 | } 333 | void GetContinousReadCommands(std::vector &cmds) const 334 | { 335 | // Add commands that are valid for specifed bus mode 336 | for (size_t i = 0; i < mCommands.size(); ++i) 337 | if (mCommands[i]->mContinuousRead) 338 | cmds.push_back(mCommands[i].get()); 339 | 340 | // If parent existed, sort and remove duplicates 341 | if (mParent) 342 | { 343 | std::sort(cmds.begin(), cmds.end()); 344 | std::unique(cmds.begin(), cmds.end()); 345 | } 346 | } 347 | SpiCmdData *GetCommand(BusMode mode, U8 code) 348 | { 349 | int key; 350 | switch (mode) 351 | { 352 | default: 353 | case SINGLE: 354 | key = code; 355 | break; 356 | case DUAL: 357 | key = 0x200 + code; 358 | break; 359 | case QUAD: 360 | key = 0x400 + code; 361 | break; 362 | } 363 | CommandMap::iterator i = mCommandMap.find(key); 364 | if (i != mCommandMap.end()) 365 | { 366 | return i->second; 367 | } 368 | else if (mParent) 369 | return mParent->GetCommand(mode, code); 370 | else 371 | { 372 | return nullptr; 373 | } 374 | } 375 | }; 376 | 377 | struct CommandSet 378 | { 379 | int mId; 380 | int mParentId; 381 | std::string mName; 382 | public: 383 | CommandSet(int id, const char *name, int parentId = -1) : mId(id), mName(name), mParentId(parentId) {} 384 | }; 385 | 386 | class SpiFlash 387 | { 388 | typedef std::vector CommandSets; 389 | CommandSets mCmdSets; 390 | CmdSet *mActiveCmdSet; 391 | SpiCmdData *mCurrentCmd; 392 | 393 | BusMode mCurBusMode; 394 | BusMode mDefBusMode; 395 | SpiMode mSpiMode; 396 | U32 mAddressBits; 397 | bool mDataIn; 398 | public: 399 | void SetSpiMode(SpiMode mode) { mSpiMode = mode; } 400 | U8 IdleClockState() const { return mSpiMode < 2 ? CLOCK_LOW : CLOCK_HIGH; } 401 | U8 Delay(U8 halfClocks) { return halfClocks + HALF_CLOCK_DELAY; } 402 | SpiFlash(); 403 | void SetDefaultBusMode(BusMode mode) { mDefBusMode = mode; } 404 | void SetCurrentBusMode(BusMode mode) { if (mode) mCurBusMode = mode; } 405 | BusMode GetCurrentBusMode() const { return mCurBusMode; } 406 | BusMode GetDefaultBusMode() const { return mDefBusMode; } 407 | void GenerateByte(U8 b, std::vector &bits); 408 | void GenerateCommandBits(SpiCmdData *cmd, std::vector &bits); 409 | void GenerateRandomCommandBits(std::vector &bits); 410 | SpiCmdData *GetCurrentCommand() const { return mCurrentCmd; } 411 | 412 | void SetCurrentCommand(SpiCmdData *cmd) { mCurrentCmd = cmd; } 413 | SpiCmdData *GetCommand(BusMode mode, U8 code) 414 | { 415 | return mActiveCmdSet ? mActiveCmdSet->GetCommand(mode, code) : nullptr; 416 | } 417 | CmdSet *GetCommandSet(uint8_t id) 418 | { 419 | CommandSets::iterator i; 420 | for (i = mCmdSets.begin(); i != mCmdSets.end(); ++i) 421 | if ((*i)->GetId() == id) 422 | return *i; 423 | return nullptr; 424 | } 425 | const std::vector &getCommandSets() const { return mCmdSets; } 426 | void GetValidCommands(std::vector &cmds) const 427 | { 428 | cmds.clear(); 429 | if (mActiveCmdSet) 430 | mActiveCmdSet->GetValidCommands(mCurBusMode, cmds); 431 | } 432 | void SelectCmdSet(uint8_t id) 433 | { 434 | mActiveCmdSet = GetCommandSet(id); 435 | } 436 | 437 | SpiFlash &operator+(const CommandSet &cmdSet) 438 | { 439 | mActiveCmdSet = new CmdSet(cmdSet.mId, cmdSet.mName, GetCommandSet(cmdSet.mParentId)); 440 | 441 | mCmdSets.push_back(mActiveCmdSet); 442 | 443 | return *this; 444 | } 445 | SpiFlash &operator+(RegisterData *reg) 446 | { 447 | if (mActiveCmdSet) 448 | mActiveCmdSet->AddRegister(reg); 449 | else 450 | delete reg; 451 | return *this; 452 | } 453 | SpiFlash &operator+(const RegisterOp ®) 454 | { 455 | if (mActiveCmdSet && mCurrentCmd) 456 | { 457 | RegisterData *regData = mActiveCmdSet->GetRegister(reg.mName.c_str()); 458 | if (regData) 459 | { 460 | mCurrentCmd->AddReg(regData); 461 | mCurrentCmd->Set(reg.mOp); 462 | } 463 | } 464 | return *this; 465 | } 466 | SpiFlash &operator+(CmdFeature feature) 467 | { 468 | if (mCurrentCmd) 469 | mCurrentCmd->Set(feature); 470 | 471 | return *this; 472 | } 473 | SpiFlash &operator+(CmdOp op) 474 | { 475 | if (mCurrentCmd) 476 | mCurrentCmd->Set(op); 477 | 478 | return *this; 479 | } 480 | SpiFlash &operator+(const DummyBytes &db) 481 | { 482 | if (mCurrentCmd) 483 | mCurrentCmd->Set(db); 484 | 485 | return *this; 486 | } 487 | SpiFlash &operator+(const DummyCycles &dc) 488 | { 489 | if (mCurrentCmd) 490 | mCurrentCmd->Set(dc); 491 | } 492 | SpiFlash &operator+(const BitField &field) 493 | { 494 | if (mActiveCmdSet) 495 | mActiveCmdSet->AddRegisterField(field); 496 | return *this; 497 | } 498 | SpiFlash &operator+(SpiCmdData *cmd) 499 | { 500 | if (mActiveCmdSet) 501 | { 502 | mActiveCmdSet->AddCommand(cmd); 503 | mCurrentCmd = cmd; 504 | } 505 | else 506 | { 507 | delete cmd; 508 | } 509 | 510 | return *this; 511 | } 512 | }; 513 | 514 | static SpiCmdData *Cmd(U8 ins, CmdMode mode, const char *n1, const char *n2, const char *n3) 515 | { 516 | return new SpiCmdData(ins, mode, n1, n2, n3); 517 | } 518 | 519 | static SpiCmdData *Cmd1(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_1, n1, n2, n3); } 520 | static SpiCmdData *Cmd2(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_2, n1, n2, n3); } 521 | static SpiCmdData *Cmd12(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_12, n1, n2, n3); } 522 | static SpiCmdData *Cmd4(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_4, n1, n2, n3); } 523 | static SpiCmdData *Cmd14(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_14, n1, n2, n3); } 524 | static SpiCmdData *Cmd24(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_24, n1, n2, n3); } 525 | static SpiCmdData *Cmd124(U8 ins, const char *n1, const char *n2 = nullptr, const char *n3 = nullptr) { return Cmd(ins, CM_124, n1, n2, n3); } 526 | 527 | extern SpiFlash spiFlash; 528 | 529 | #endif //SPIFLASH_H 530 | -------------------------------------------------------------------------------- /source/SpiFlashAnalyzer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include "SpiFlashAnalyzer.h" 25 | #include "SpiFlashAnalyzerSettings.h" 26 | #include 27 | #include "SpiFlashAnalyzerResults.h" 28 | 29 | #include "SpiFlash.h" 30 | 31 | SpiFlashAnalyzer::SpiFlashAnalyzer() 32 | : Analyzer2(), 33 | mSettings(new SpiFlashAnalyzerSettings()), 34 | mSimulationInitilized(false) 35 | { 36 | SetAnalyzerSettings(mSettings.get()); 37 | } 38 | 39 | SpiFlashAnalyzer::~SpiFlashAnalyzer() 40 | { 41 | KillThread(); 42 | } 43 | 44 | AnalyzerChannelData *SpiFlashAnalyzer::GetAnalyzerChannelData(Channel& channel) 45 | { 46 | if (channel == UNDEFINED_CHANNEL) 47 | return nullptr; 48 | else 49 | return Analyzer::GetAnalyzerChannelData(channel); 50 | } 51 | 52 | void SpiFlashAnalyzer::SetupResults() 53 | { 54 | mResults.reset(new SpiFlashAnalyzerResults(this, mSettings.get())); 55 | SetAnalyzerResults(mResults.get()); 56 | 57 | // Whole command goes to CS or clock 58 | if (mSettings->mChipSelect != UNDEFINED_CHANNEL) 59 | mResults->AddChannelBubblesWillAppearOn(mSettings->mChipSelect); 60 | else 61 | mResults->AddChannelBubblesWillAppearOn(mSettings->mClock); 62 | 63 | if (mSettings->mMosi != UNDEFINED_CHANNEL) 64 | mResults->AddChannelBubblesWillAppearOn(mSettings->mMosi); 65 | 66 | if (mSettings->mMiso != UNDEFINED_CHANNEL) 67 | mResults->AddChannelBubblesWillAppearOn(mSettings->mMiso); 68 | } 69 | 70 | void SpiFlashAnalyzer::AddFrame(U64 start, U64 end, U64 d1, U64 d2, U8 type, U8 flags) 71 | { 72 | Frame f; 73 | f.mStartingSampleInclusive = S64(start); 74 | f.mEndingSampleInclusive = S64(end); 75 | f.mData1 = d1; 76 | f.mData2 = d2; 77 | f.mFlags = flags; 78 | f.mType = type; 79 | 80 | mResults->AddFrame(f); 81 | mResults->CommitResults(); 82 | } 83 | 84 | // TODO: Remove this once there is no going back in time 85 | U64 pos; 86 | 87 | void SpiFlashAnalyzer::Setup() 88 | { 89 | mLockedCmd = nullptr; 90 | mChipSelect = GetAnalyzerChannelData(mSettings->mChipSelect); 91 | mClock = GetAnalyzerChannelData(mSettings->mClock); 92 | mMosi = GetAnalyzerChannelData(mSettings->mMosi); 93 | mMiso = GetAnalyzerChannelData(mSettings->mMiso); 94 | mD2 = GetAnalyzerChannelData(mSettings->mD2); 95 | mD3 = GetAnalyzerChannelData(mSettings->mD3); 96 | if (mSettings->mSpiMode == 0) 97 | mClockIdleState = BIT_LOW; 98 | else if (mSettings->mSpiMode == 3) 99 | mClockIdleState = BIT_HIGH; 100 | 101 | mDefaultBusMode = BusMode(mSettings->mBusMode); 102 | mCurrentBusMode = mDefaultBusMode; 103 | // Continues read mode selected as starting point 104 | U8 manufacturer = (U8)(mSettings->mContinuousRead >> 8); 105 | U8 code = (U8)mSettings->mContinuousRead; 106 | CmdSet *cmdSet = spiFlash.GetCommandSet(manufacturer); 107 | if (cmdSet) 108 | { 109 | SpiCmdData *cmd = cmdSet->GetCommand(mCurrentBusMode, code); 110 | if (cmd != NULL) 111 | { 112 | mLockedCmd = cmd; 113 | mCurrentBusMode = BusMode(cmd->mModeData); 114 | } 115 | } 116 | mCachedClockCount = 0; 117 | pos = 0; 118 | } 119 | 120 | void SpiFlashAnalyzer::AdvanceDataToAbsPosition(U64 AbsolutePosition) 121 | { 122 | if (pos > AbsolutePosition) 123 | { 124 | return; 125 | } 126 | pos = AbsolutePosition; 127 | if (mMosi) mMosi->AdvanceToAbsPosition(AbsolutePosition); 128 | if (mMiso) mMiso->AdvanceToAbsPosition(AbsolutePosition); 129 | if (mD2) mD2->AdvanceToAbsPosition(AbsolutePosition); 130 | if (mD3) mD3->AdvanceToAbsPosition(AbsolutePosition); 131 | } 132 | 133 | void SpiFlashAnalyzer::CacheDropOlderClocks(U64 limit) 134 | { 135 | int i; 136 | 137 | for (i = 0; i < mCachedClockCount; ++i) 138 | { 139 | if (mCachedClocks[i] >> 1 >= limit) 140 | { 141 | if (i > 0) 142 | { 143 | memmove(mCachedClocks, mCachedClocks + i, 144 | (mCachedClockCount - i) * sizeof(mCachedClocks[0])); 145 | } 146 | break; 147 | } 148 | } 149 | mCachedClockCount -= i; 150 | } 151 | 152 | void SpiFlashAnalyzer::CacheClock(int num, U64 lowerLimit) 153 | { 154 | if (lowerLimit) 155 | CacheDropOlderClocks(lowerLimit); 156 | 157 | // No cached clocks, move clock forward 158 | if (mClock->GetSampleNumber() < lowerLimit) 159 | mClock->AdvanceToAbsPosition(lowerLimit); 160 | 161 | while (mCachedClockCount < num && mClock->DoMoreTransitionsExistInCurrentData()) 162 | { 163 | mClock->AdvanceToNextEdge(); 164 | if (mClock->GetBitState() == BIT_HIGH) 165 | mCachedClocks[mCachedClockCount++] = (mClock->GetSampleNumber() << 1) + 1; 166 | else 167 | mCachedClocks[mCachedClockCount++] = mClock->GetSampleNumber() << 1; 168 | } 169 | } 170 | 171 | void SpiFlashAnalyzer::AdvanceToCommandStart() 172 | { 173 | // If CS is present just move to next falling edge 174 | if (mChipSelect != NULL) 175 | { 176 | if (mChipSelect->GetBitState() == BIT_HIGH) 177 | { 178 | mChipSelect->AdvanceToNextEdge(); 179 | } 180 | else 181 | { 182 | mChipSelect->AdvanceToNextEdge(); 183 | mChipSelect->AdvanceToNextEdge(); 184 | } 185 | mCommandStart = mChipSelect->GetSampleNumber(); 186 | 187 | CacheClock(16, mCommandStart); 188 | if (mCachedClockCount > 0) 189 | { 190 | bool clockHigh = mCachedClocks[0] & 1; 191 | // If mode is 0 or 3 and clock state is not matching mark error 192 | if ((mSettings->mSpiMode == 0 && !clockHigh) || 193 | (mSettings->mSpiMode == 3 && clockHigh)) 194 | { 195 | mResults->AddMarker(mCommandStart, AnalyzerResults::ErrorSquare, mSettings->mClock); 196 | } 197 | else if (mSettings->mSpiMode == 0xFF) 198 | // For auto mode take current clock state as idle state 199 | mClockIdleState = clockHigh ? BIT_HIGH : BIT_LOW; 200 | } 201 | 202 | // Command ends at next rising edge of CS or at the end of data 203 | if (mChipSelect->DoMoreTransitionsExistInCurrentData()) 204 | { 205 | mChipSelect->AdvanceToNextEdge(); 206 | mCommandEnd = mChipSelect->GetSampleNumber(); 207 | } 208 | else 209 | mCommandEnd = ~0; 210 | } 211 | else 212 | { 213 | // TODO: Rethink clocks !!! 214 | // Hardware generated clock should have some pattern 215 | U64 edges[10]; 216 | 217 | if (mSettings->mSpiMode == 0 && mClock->GetBitState() == BIT_HIGH) 218 | mClock->AdvanceToNextEdge(); 219 | else if (mSettings->mSpiMode == 3 && mClock->GetBitState() == BIT_LOW) 220 | mClock->AdvanceToNextEdge(); 221 | else 222 | mClockIdleState = BIT_LOW; 223 | 224 | U64 sample = mClock->GetSampleNumber(); 225 | // Assume that clock is in idle now 226 | mClock->AdvanceToNextEdge(); 227 | edges[0] = mClock->GetSampleNumber(); // rising edge 228 | mClock->AdvanceToNextEdge(); 229 | edges[1] = mClock->GetSampleNumber(); // falling edge 230 | while (true) 231 | { 232 | mClock->AdvanceToNextEdge(); 233 | edges[2] = mClock->GetSampleNumber(); // rising edge 234 | mClock->AdvanceToNextEdge(); 235 | edges[3] = mClock->GetSampleNumber(); // falling edge 236 | int d1 = int(edges[1] - edges[0]); 237 | int d2 = int(edges[2] - edges[1]); 238 | int d3 = int(edges[3] - edges[2]); 239 | if (d3 == 0) 240 | return; 241 | // If positive pulses differe more then 10 % and 2 samples 242 | // or negative puls differes from positive more than 30 % and 2 samples 243 | // lets move to place where clock is more stable 244 | if ((abs(d1 - d3) > 2 && (abs(d1 - d2) > d1 / 10)) || 245 | (abs(d2 - d1) > 2 && (abs(d1 - d2) > d1 / 30))) 246 | { 247 | edges[0] = edges[2]; 248 | edges[1] = edges[3]; 249 | continue; 250 | } 251 | mClock->AdvanceToAbsPosition(edges[0]); 252 | mCommandStart = mClock->GetSampleNumber(); 253 | break; 254 | } 255 | } 256 | AdvanceDataToAbsPosition(mCommandStart); 257 | } 258 | 259 | U8 SpiFlashAnalyzer::GetBits(BusMode busMode, bool dirIn) 260 | { 261 | U8 b = 0; 262 | 263 | if (busMode == SINGLE) 264 | { 265 | if (dirIn) 266 | { 267 | if (mMiso) 268 | b = mMiso->GetBitState() == BIT_HIGH ? 1 : 0; 269 | } 270 | else 271 | { 272 | if (mMosi) 273 | b = mMosi->GetBitState() == BIT_HIGH ? 1 : 0; 274 | } 275 | } 276 | else 277 | { 278 | if (mMosi) 279 | b = mMosi->GetBitState() == BIT_HIGH ? 1 : 0; 280 | if (mMiso) 281 | b |= mMiso->GetBitState() == BIT_HIGH ? 2 : 0; 282 | } 283 | if (busMode == QUAD) 284 | { 285 | if (mD2) 286 | b |= mD2->GetBitState() == BIT_HIGH ? 4 : 0; 287 | if (mD3) 288 | b |= mD3->GetBitState() == BIT_HIGH ? 8 : 0; 289 | } 290 | 291 | return b; 292 | } 293 | 294 | int SpiFlashAnalyzer::ExtractBits(U64 &start, U64 &end, U32 &val, U8 neededBits) 295 | { 296 | BusMode busMode = mCurrentBusMode; 297 | U8 bitCount = 0; 298 | val = 0; 299 | int i; 300 | int clockEdgesPerByte = 2 * neededBits / busMode; 301 | 302 | CacheClock(clockEdgesPerByte, mCommandStart); 303 | 304 | // Start time of first clock edge (rising or falling) 305 | start = mCachedClocks[0] >> 1; 306 | 307 | // Not enough clocks to form a byte, and those clocks are in active CS? 308 | if (mCachedClockCount < clockEdgesPerByte || (mCachedClocks[clockEdgesPerByte - 1] >> 1) > mCommandEnd) 309 | { 310 | if (mCachedClockCount) 311 | { 312 | end = mCachedClocks[mCachedClockCount - 1] >> 1; 313 | if (end > mCommandEnd) 314 | end = mCommandEnd; 315 | } 316 | return -1; 317 | } 318 | 319 | // Let i point to rising edge time in table 320 | i = (mCachedClocks[0] & 1) ? 0 : 1; 321 | 322 | while (bitCount < neededBits) 323 | { 324 | AdvanceDataToAbsPosition(mCachedClocks[i] >> 1); 325 | mResults->AddMarker(mCachedClocks[i] >> 1, AnalyzerResults::UpArrow, mSettings->mClock); 326 | val <<= busMode; 327 | val |= GetBits(busMode, mDirIn); 328 | bitCount += busMode; 329 | i += 2; 330 | } 331 | end = mCachedClocks[clockEdgesPerByte - 1] >> 1; 332 | CacheDropOlderClocks(end + 1); 333 | 334 | return 0; 335 | } 336 | 337 | int SpiFlashAnalyzer::ExtractMosiMiso(U64 &start, U64 &end, U8 &mosi, U8 &miso) 338 | { 339 | BusMode busMode = mCurrentBusMode; 340 | U8 bitCount; 341 | int i; 342 | int ret = 0; 343 | int clocksPerByte = 8 * 2; 344 | mosi = 0; 345 | miso = 0; 346 | 347 | CacheClock(clocksPerByte, mCommandStart); 348 | 349 | // Start time of first clock edge (rising or falling) 350 | start = mCachedClocks[0] >> 1; 351 | 352 | // Not enough clocks to form a byte, and those clocks are in active CS? 353 | if (mCachedClockCount < clocksPerByte || (mCachedClocks[clocksPerByte - 1] >> 1) > mCommandEnd) 354 | { 355 | end = mCachedClocks[mCachedClockCount - 1] >> 1; 356 | if (end > mCommandEnd) 357 | end = mCommandEnd; 358 | ret = -1; 359 | } 360 | else 361 | { 362 | // Let i point to rising edge time in table 363 | i = (mCachedClocks[0] & 1) ? 0 : 1; 364 | 365 | for (bitCount = 0; bitCount < 8; ++bitCount, i += 2) 366 | { 367 | AdvanceDataToAbsPosition(mCachedClocks[i] >> 1); 368 | if (mMosi) 369 | mosi = (mosi << 1) + (mMosi->GetBitState() == BIT_HIGH ? 1 : 0); 370 | if (mMiso) 371 | miso = (miso << 1) + (mMiso->GetBitState() == BIT_HIGH ? 1 : 0); 372 | } 373 | end = mCachedClocks[clocksPerByte - 1] >> 1; 374 | } 375 | CacheDropOlderClocks(end + 1); 376 | 377 | return ret; 378 | } 379 | 380 | void SpiFlashAnalyzer::AnalyzeCommandBits() 381 | { 382 | int b; 383 | 384 | union 385 | { 386 | SpiCmdData *data; 387 | intptr_t code; 388 | } cmd; 389 | U64 cmdExtra; 390 | 391 | U32 val; 392 | 393 | U32 addr = 0; 394 | 395 | U64 start; 396 | U64 end; 397 | 398 | U8 m; 399 | 400 | cmd.data = nullptr; 401 | mResults->CommitPacketAndStartNewPacket(); 402 | 403 | mDirIn = false; 404 | 405 | do 406 | { 407 | cmdExtra = 0; 408 | if (mLockedCmd != nullptr) 409 | cmd.data = mLockedCmd; 410 | else 411 | { 412 | b = ExtractBits(start, end, val, 8); 413 | if (b < 0) 414 | { 415 | // Not enough bits for decoding command set value that is more then byte 416 | // but not enough for valid pointer 417 | cmd.code = 0x100; 418 | break; 419 | } 420 | 421 | cmd.data = spiFlash.GetCommand(mCurrentBusMode, U8(val)); 422 | if (cmd.data == nullptr) 423 | cmd.code = (int)val; 424 | 425 | // Add command to MOSI line 426 | AddFrame(start, end, val, reinterpret_cast(cmd.data), FT_CMD_BYTE, 0); 427 | } 428 | 429 | if (cmd.code > 0x100) 430 | { 431 | UpdateBusMode((BusMode)cmd.data->mModeArgs); 432 | if (cmd.data->mAddressBits) 433 | { 434 | U32 addressLength = (cmd.data->mAddressBits != 0xFF) ? cmd.data->mAddressBits : mSettings->mAddressLength; 435 | addr = 0; 436 | if (ExtractBits(start, end, addr, addressLength) < 0) 437 | break; 438 | AddFrame(start, end, addr, 0, FT_OUT_ADDR24, 0); 439 | cmdExtra = U64(addr) << 24; 440 | } 441 | if (cmd.data->mContinuousRead) 442 | { 443 | if (ExtractBits(start, end, val, 8) < 0) 444 | break; 445 | m = U8(val); 446 | mLockedCmd = ((m & 0x30) == 0x20) ? cmd.data : nullptr; 447 | AddFrame(start, end, val, 0, FT_M, 0); 448 | } 449 | 450 | U64 dummyStart = 0; 451 | U64 dummyEnd = 0; 452 | 453 | if (cmd.data->mDummyBytes) 454 | { 455 | if (ExtractBits(dummyStart, dummyEnd, val, cmd.data->mDummyCount * 8) < 0) 456 | break; 457 | } 458 | else if (cmd.data->mDummyCycles) 459 | { 460 | if (ExtractBits(dummyStart, dummyEnd, val, cmd.data->mDummyCycles) < 0) 461 | break; 462 | } 463 | 464 | // Dummy cycles or byte found 465 | if (dummyEnd) 466 | { 467 | AddFrame(dummyStart, dummyEnd, val, 0, FT_DUMMY, 0); 468 | end = dummyEnd; 469 | } 470 | 471 | // Change bus mode if command require change for data phase 472 | UpdateBusMode(BusMode(cmd.data->mModeData)); 473 | 474 | switch (cmd.data->mCmdOp) 475 | { 476 | case OP_DATA_WRITE: 477 | while (ExtractBits(start, end, val, 8) >= 0) 478 | { 479 | AddFrame(start, end, val, 0, FT_OUT_BYTE, 0); 480 | cmdExtra++; 481 | } 482 | break; 483 | case OP_DATA_READ: 484 | mDirIn = true; 485 | while (ExtractBits(start, end, val, 8) >= 0) 486 | { 487 | AddFrame(start, end, 0, val, FT_IN_BYTE, 0); 488 | cmdExtra++; 489 | } 490 | break; 491 | case OP_REG_WRITE: 492 | while (ExtractBits(start, end, val, 8) >= 0) 493 | { 494 | AddFrame(start, end, val, 495 | reinterpret_cast(cmd.data->GetRegister(size_t(cmdExtra))), FT_OUT_REG, 0); 496 | cmdExtra++; 497 | } 498 | break; 499 | case OP_REG_READ: 500 | mDirIn = true; 501 | while (ExtractBits(start, end, val, 8) >= 0) 502 | { 503 | AddFrame(start, end, reinterpret_cast(cmd.data->GetRegister(size_t(cmdExtra))), 504 | val, FT_IN_REG, 0); 505 | cmdExtra++; 506 | } 507 | break; 508 | } 509 | // Commands like Enter QPI or Exit QPI change bus mode 510 | if (cmd.data->mModeChange) 511 | mDefaultBusMode = BusMode(cmd.data->mModeChange); 512 | } 513 | else if (cmd.code < 0x100) 514 | { 515 | U8 miso, mosi; 516 | while (ExtractMosiMiso(start, end, mosi, miso) >= 0) 517 | AddFrame(start, end, mosi, miso, FT_IN_OUT, 0); 518 | } 519 | } while (0); 520 | 521 | if (cmd.code != 0x100) 522 | { 523 | AddFrame(mCommandStart, mCommandEnd, cmdExtra, reinterpret_cast(cmd.data), FT_CMD, 0); 524 | ReportProgress(mCommandEnd); 525 | } 526 | 527 | 528 | // Set default bus mode 529 | mCurrentBusMode = mDefaultBusMode; 530 | } 531 | 532 | void SpiFlashAnalyzer::WorkerThread() 533 | { 534 | Setup(); 535 | 536 | for (;;) 537 | { 538 | AdvanceToCommandStart(); 539 | AnalyzeCommandBits(); 540 | CheckIfThreadShouldExit(); 541 | } 542 | } 543 | 544 | bool SpiFlashAnalyzer::NeedsRerun() 545 | { 546 | return false; 547 | } 548 | 549 | U32 SpiFlashAnalyzer::GenerateSimulationData(U64 minimum_sample_index, U32 device_sample_rate, SimulationChannelDescriptor** simulation_channels) 550 | { 551 | if (mSimulationInitilized == false) 552 | { 553 | mSimulationDataGenerator.Initialize(GetSimulationSampleRate(), mSettings.get()); 554 | mSimulationInitilized = true; 555 | } 556 | 557 | return mSimulationDataGenerator.GenerateSimulationData(minimum_sample_index, device_sample_rate, simulation_channels); 558 | } 559 | 560 | U32 SpiFlashAnalyzer::GetMinimumSampleRateHz() 561 | { 562 | return 100000; 563 | } 564 | 565 | const char* SpiFlashAnalyzer::GetAnalyzerName() const 566 | { 567 | return "SPI Flash"; 568 | } 569 | 570 | const char* GetAnalyzerName() 571 | { 572 | return "SPI Flash"; 573 | } 574 | 575 | Analyzer* CreateAnalyzer() 576 | { 577 | return new SpiFlashAnalyzer(); 578 | } 579 | 580 | void DestroyAnalyzer(Analyzer* analyzer) 581 | { 582 | delete analyzer; 583 | } 584 | -------------------------------------------------------------------------------- /source/SpiFlash.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright(c) 2017 Jerzy Kasenberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files(the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions : 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include 25 | #include "SpiFlash.h" 26 | 27 | void SpiFlash::GenerateByte(U8 b, std::vector &bits) 28 | { 29 | // Bits for all the lines at once 0 - CS, 1 - CLK, 2-5 data bits 30 | U8 lines = 0; 31 | int n; 32 | // Mask for extracting bits x 1b 2b x 4b 33 | static const U8 mask[5] = { 0, 0x80, 0xC0, 0, 0xF0 }; 34 | // row 0 for output, row 1 for input 35 | // CS is on bit 0 36 | // CLK on bit 1 37 | // Data line are on bits 2-5 hence shifts 38 | static const U8 shif[2][5] = { { 0, 5, 4, 0, 2 }, {0, 4, 4, 0, 2} }; 39 | 40 | for (int i = 0; i < 8; ) 41 | { 42 | n = 0; 43 | // Extract as many bits as bus mode wants, they will be left aligned 44 | lines = b & mask[mCurBusMode]; 45 | // shift right to bits 2-5 (for quad), 2-3 (for dual), o 2 or 3 for single mode 46 | lines >>= shif[mDataIn][mCurBusMode]; 47 | 48 | b <<= mCurBusMode; 49 | i += mCurBusMode; 50 | // Add lines to history, CS is low all the time 51 | bits.push_back(CS_LOW | lines | CLOCK_LOW); 52 | // Add same lines with clock high 53 | bits.push_back(CS_LOW | lines | CLOCK_HIGH); 54 | } 55 | } 56 | 57 | void SpiFlash::GenerateCommandBits(SpiCmdData *cmd, std::vector &bits) 58 | { 59 | int n; 60 | mDataIn = false; 61 | // Add some delay before CS goes low 62 | bits.push_back(Delay(3 + rand() % 10)); 63 | 64 | // generate cmd bits if mActiveCmd in not set 65 | if (mCurrentCmd == nullptr) 66 | { 67 | GenerateByte(cmd->GetCode(), bits); 68 | } 69 | 70 | // if bus witdh changes after command code change current bus mode 71 | if (cmd->mModeArgs) 72 | mCurBusMode = BusMode(cmd->mModeArgs); 73 | 74 | // generate address 75 | if (cmd->mAddressBits) 76 | { 77 | uint32_t addr = rand(); 78 | U32 addressBits = (cmd->mAddressBits != 0xFF) ? cmd->mAddressBits : mAddressBits; 79 | if (addressBits > 24) 80 | GenerateByte(U8(addr >> 24), bits); 81 | if (addressBits > 16) 82 | GenerateByte(U8(addr >> 16), bits); 83 | if (addressBits > 8) 84 | GenerateByte(U8(addr >> 8), bits); 85 | GenerateByte(U8(addr), bits); 86 | } 87 | 88 | // for continuous read mode command generate M bits 89 | if (cmd->mContinuousRead) 90 | { 91 | // 3/4 times stay in continuous read mode 92 | if (rand() % 4) 93 | { 94 | GenerateByte(0xAF, bits); 95 | mCurrentCmd = cmd; 96 | } 97 | else 98 | { 99 | // 1/4 times go to command mode again 100 | GenerateByte(0xFF, bits); 101 | mCurrentCmd = nullptr; 102 | } 103 | } 104 | 105 | // Add some dummy bytes if needed 106 | if (cmd->mDummyBytes) 107 | { 108 | for (int i = 0; i < cmd->mDummyCount; ++i) 109 | GenerateByte(0xFF, bits); 110 | } 111 | // or maybe some dummy cycles 112 | else if (cmd->mDummyCycles) 113 | { 114 | for (int i = 0; i < cmd->mDummyCount; ++i) 115 | { 116 | // Add lines to history 117 | bits.push_back(CS_LOW | MOSI_HIGH | MISO_HIGH | D2_HIGH | D3_HIGH | CLOCK_LOW); 118 | // Add same lines with clock high 119 | bits.push_back(CS_LOW | MOSI_HIGH | MISO_HIGH | D2_HIGH | D3_HIGH | CLOCK_HIGH); 120 | } 121 | } 122 | 123 | // Switch do other bus mode if this is 1-1-2 or 1-1-4 command 124 | if (cmd->mModeData) 125 | mCurBusMode = BusMode(cmd->mModeData); 126 | 127 | // Generate data for commands that have it 128 | mDataIn = (cmd->mCmdOp == OP_REG_READ || cmd->mCmdOp == OP_DATA_READ); 129 | switch (cmd->mCmdOp) 130 | { 131 | case OP_REG_READ: 132 | case OP_REG_WRITE: 133 | // For register read or write just one or to bytes 134 | n = (int)cmd->RegisterCount(); 135 | break; 136 | case OP_DATA_READ: 137 | case OP_DATA_WRITE: 138 | // For data read or write generate up to 50 bytes 139 | n = 1 + rand() % 50; 140 | break; 141 | default: 142 | // Commands does not have any additional data 143 | n = 0; 144 | } 145 | 146 | // Genereate data 147 | for (int i = 0; i < n; ++i) 148 | GenerateByte(U8(rand()), bits); 149 | 150 | // If command changed bus mode update it 151 | // (comands like Enter/Exit QPI mode) 152 | if (cmd->mModeChange) 153 | mDefBusMode = BusMode(cmd->mModeChange); 154 | 155 | // Switch to default bus mode 156 | mCurBusMode = mDefBusMode; 157 | } 158 | 159 | void SpiFlash::GenerateRandomCommandBits(std::vector &bits) 160 | { 161 | std::vector cmds; 162 | 163 | if (mCurrentCmd) 164 | GenerateCommandBits(mCurrentCmd, bits); 165 | else 166 | { 167 | GetValidCommands(cmds); 168 | SpiCmdData *cmd = mActiveCmdSet->GetCommand(mCurBusMode, cmds[rand() % cmds.size()]); 169 | if (cmd) 170 | GenerateCommandBits(cmd, bits); 171 | } 172 | } 173 | 174 | void addCommands(SpiFlash &spiFlash) 175 | { 176 | spiFlash 177 | + CommandSet(0, "not set") 178 | + Register("Status Register-1", 8) + Bit(7, "SRP0") + Bit(1, "WEL") + Bit(0, "BUSY") 179 | + Register("Status Register-2", 8) + Bit(7, "SUS") + Bit(1, "QE") + Bit(0, "SRP1") 180 | 181 | + Cmd14(0x06, "WREN", "Write Enable") 182 | + Cmd14(0x04, "WRDI", "Write Disable") 183 | + Cmd14(0x05, "RDSR", "Read status register-1") + RegisterRead("Status Register-1") 184 | + Cmd14(0x35, "RS2", "Read status register-2") + RegisterWrite("Status Register-2") 185 | + Cmd14(0x01, "WS1", "Write status register-1") + RegisterWrite("Status Register-1") + RegisterWrite("Status Register-2") + 186 | + Cmd14(0x31, "WS2", "Write status register-2") + RegisterWrite("Status Register-2") 187 | + Cmd1(0x03, "R", "Read Data") + ADDR + OP_DATA_READ 188 | + Cmd1(0x0B, "R", "Fast Read") + ADDR + DummyBytes(1) + OP_DATA_READ 189 | + Cmd1(0x3B, "R", "R 1-1-2", "Fast Read Dual Ouput") + ADDR + DummyBytes(1) + DUAL_DATA + OP_DATA_READ 190 | + Cmd1(0x6B, "R", "R 1-1-4", "Fast Read Quad Output") + ADDR + DummyBytes(1) + QUAD_DATA + OP_DATA_READ 191 | + Cmd1(0xBB, "R", "R 1-2-2", "Fast Read Dual I/O") + DUAL_IO + ADDR + M + OP_DATA_READ 192 | + Cmd1(0xEB, "R", "R 1-4-4", "Fast Read Quad I/O") + QUAD_IO + ADDR + M + DummyBytes(2) + OP_DATA_READ 193 | + Cmd14(0x02, "PP", "Page Program") + ADDR + OP_DATA_WRITE 194 | + Cmd14(0x20, "SE", "Sector erase") + ADDR 195 | + Cmd14(0x52, "BE", "Block erase") + ADDR 196 | + Cmd14(0xD8, "BE", "BE64", "64KB Block erase") + ADDR 197 | + Cmd14(0x60, "CE", "Chip erase") 198 | + Cmd14(0xC7, "CE", "Chip erase") 199 | + Cmd1(0x5A, "SFDP", "Read SFDP Register") + ADDR + DummyBytes(1) + OP_DATA_READ 200 | + Cmd14(0x75, "SUSP", "Erase/Program Suspend") 201 | + Cmd14(0x7A, "RESM", "Erase/Program Resume") 202 | + Cmd14(0xB9, "DN", "Power Down") 203 | + Cmd14(0x9F, "JID", "Read JEDEC ID") + OP_DATA_READ 204 | + Cmd1(0x90, "MFID", "Read manufacturer, Device ID") + ADDR + OP_DATA_READ 205 | + Cmd14(0x66, "RSTEN", "Enable Reset") 206 | + Cmd14(0x99, "RST", "Reset") 207 | + Cmd14(0xAB, "UP", "Release Power Down") + DummyBytes(3) + OP_DATA_READ 208 | + CommandSet(0xEF, "Winbond", 0) 209 | + Register("Status Register-1", 8) + Bit(7, "SRP0") + Bit(6, "TPB") + Bit(5, "TP") + Bit(4, 2, "BPB") + Bit(1, "WEL") + Bit(0, "BUSY") 210 | + Register("Status Register-2", 8) + Bit(7, "SUS") + Bit(6, "CMP") + Bit(5, 3, "LB") + Bit(1, "QE") + Bit(0, "SRP1") 211 | + Register("Status Register-3", 8) + Bit(7, "HOLD/RESET") + Bit(6, 5, "DRV") + Bit(2, "WPS") 212 | 213 | + Cmd14(0x50, "WRENVSR", "Write Enable for Volatile Status Register") 214 | + Cmd14(0x35, "RS2", "Read status register-2") + RegisterRead("Status Register-2") 215 | + Cmd14(0x15, "RS3", "Read status register-3") + RegisterRead("Status Register-3") 216 | + Cmd14(0x01, "WS1", "Write status register-1") + RegisterWrite("Status Register-1") + RegisterWrite("Status Register-2") 217 | + Cmd14(0x31, "WS2", "Write status register-2") + RegisterWrite("Status Register-2") 218 | + Cmd14(0x11, "WS3", "Write status register-3") + RegisterWrite("Status Register-3") 219 | + Cmd4(0xEB, "R", "R 1-4-4", "Fast Read Quad I/O") + QUAD_IO + ADDR + M + OP_DATA_READ 220 | + Cmd14(0xE7, "R", "R 1-4-4", "Word Read Quad I/O") + QUAD_IO + ADDR + M + DummyBytes(1) + OP_DATA_READ 221 | + Cmd14(0xE3, "R", "R 1-4-4", "Octal Word Read Quad I/O") + QUAD_IO + ADDR + M + OP_DATA_READ 222 | + Cmd14(0x36, "Individual Block/Sector Lock") + ADDR 223 | + Cmd14(0x39, "Individual Block/Sector Unlock") + ADDR 224 | + Cmd14(0x3D, "Read Block/Sector Lock") + ADDR 225 | + Cmd14(0x7E, "Global Block/Sector Lock") 226 | + Cmd14(0x98, "Global Block/Sector Unlock") 227 | 228 | + Cmd1(0x77, "Set Burst with Wrap") + QUAD_IO + DummyBytes(3) + OP_DATA_WRITE 229 | + Cmd1(0x32, "QPP", "Quad Input Page Program") + QUAD_DATA + ADDR + OP_DATA_WRITE 230 | + Cmd1(0x92, "MFID", "Read manufacturer, Device ID DUAL I/O") + DUAL_IO + ADDR + DummyBytes(1) + OP_DATA_READ 231 | + Cmd1(0x94, "MFID", "Read manufacturer, Device ID QUAD I/O") + QUAD_IO + ADDR + DummyBytes(3) + OP_DATA_READ 232 | + Cmd1(0x4B, "ID", "Read Unique ID number") + DummyBytes(4) + OP_DATA_READ 233 | + Cmd1(0x44, "Erase Security Registers") + ADDR 234 | + Cmd1(0x42, "Program Security Registers") + ADDR + OP_DATA_WRITE 235 | + Cmd1(0x48, "Read Security Registers") + ADDR + DummyBytes(1) + OP_DATA_READ 236 | + Cmd1(0x38, "*4", "QPI", "Enter QPI Mode") + SET_QUAD 237 | 238 | + Cmd4(0x0B, "R", "R 4-4-4", "Fast Read") + ADDR + DummyBytes(1) + OP_DATA_READ 239 | + Cmd4(0xC0, "SRP", "Set Read Parameters") + OP_DATA_WRITE 240 | + Cmd4(0x0C, "BRW", "Burst Read with Wrap") + ADDR + M + DummyBytes(1) + OP_DATA_READ 241 | 242 | + Cmd14(0xff, "*1", "Exit QPI Mode") + SET_SINGLE 243 | + CommandSet(0xC2, "Macronix", 0) 244 | + Register("Status Register-1", 8) + Bit(7, "SRWD") + Bit(6, "QE") + Bit(5, 2, "BPB") + Bit(1, "WEL") + Bit(0, "WIP") 245 | + Register("Configuration Register-1", 8) + Bit(6, "DC") + Bit(3, "TB") 246 | + Register("Configuration Register-2", 8) + Bit(1, "L/H") 247 | + Register("Security Register", 8) + Bit(6, "E_FAIL") + Bit(5, "P_FAIL") + Bit(3, "ESB") + Bit(2, "PSB") + Bit(1, "LDSO") + Bit(0, "SOTP") 248 | + Cmd1(0x01, "WSRS", "Write status register") + RegisterWrite("Status Register-1") + RegisterWrite("Configuration Register-1") + RegisterWrite("Configuration Register-2") 249 | + Cmd1(0x05, "RDSR", "Read status register-1") + RegisterRead("Status Register-1") 250 | + Cmd1(0x15, "RDCR", "Read configuration register") + RegisterRead("Configuration Register-1") + RegisterRead("Configuration Register-2") 251 | + Cmd1(0xB0, "SUSP", "Erase/Program Suspend") 252 | + Cmd1(0x30, "RESM", "Erase/Program Resume") 253 | + Cmd1(0xC0, "SBL", "Set Burst Length") + OP_DATA_WRITE 254 | + Cmd1(0xB1, "ENSO", "Enter Secured OTP") 255 | + Cmd1(0xC1, "EXSO", "Exit Secured OTP") 256 | + Cmd1(0x2B, "RDSCUR", "Read Security Register") + RegisterRead("Security Register") 257 | + Cmd1(0x2F, "WRSCUR", "Write Security Register") + RegisterWrite("Security Register") 258 | + Cmd1(0xAB, "RES", "Read Electronic ID") + DummyBytes(3) + OP_DATA_READ 259 | + Cmd1(0x32, "QPP", "Quad Input Page Program") + QUAD_DATA + ADDR + OP_DATA_WRITE 260 | + Cmd1(0x38, "QPP", "Quad I/O Page Program") + QUAD_IO + ADDR + OP_DATA_WRITE 261 | 262 | + CommandSet(0xC8, "GigaDevice", 0xEF) 263 | + Register("Status Register-1", 8) + Bit(7, "SRP0") + Bit(6, 2, "BPB") + Bit(1, "WEL") + Bit(0, "BUSY") 264 | + Register("Status Register-2", 8) + Bit(7, "SUS1") + Bit(6, "CMP") + Bit(5, 3, "LB") + Bit(2, "SUS2") + Bit(1, "QE") + Bit(0, "SRP1") 265 | + CommandSet(0x1F, "Adesto", 0) 266 | + Cmd14(0xB1, "ENSO", "Enter Secured OTP") 267 | + Cmd14(0xC1, "EXSO", "Exit Secured OTP") 268 | + Cmd14(0x2B, "RDSCUR", "Read Security Register") 269 | + Cmd14(0x2F, "WRSCUR", "Write Security Register") 270 | + Cmd1(0x38, "*4", "QPI", "Enter QPI Mode") + SET_QUAD 271 | + Cmd4(0xff, "*1", "Exit QPI Mode") + SET_SINGLE 272 | + Cmd4(0x0C, "BRW", "Burst Read with Wrap") + ADDR + M + DummyBytes(1) + OP_DATA_READ 273 | + Cmd4(0xC0, "SRP", "Set Read Parameters") + OP_DATA_WRITE 274 | + Cmd14(0x33, "QPP", "Quad Input Page Program") + QUAD_DATA + ADDR + OP_DATA_WRITE 275 | + Cmd1(0x94, "MFID", "Read manufacturer, Device ID QUAD I/O") + QUAD_IO + ADDR + DummyBytes(3) + OP_DATA_READ 276 | + Cmd14(0xE7, "R", "R 1-4-4", "Word Read Quad I/O") + QUAD_IO + ADDR + M + DummyBytes(1) + OP_DATA_READ 277 | + Cmd1(0x77, "Set Burst with Wrap") + QUAD_IO + DummyBytes(3) + OP_DATA_WRITE 278 | 279 | + CommandSet(0x01, "Cypress", 0) 280 | + Register("Status Register-1", 8) + Bit(7, "SRP0") + Bit(6, "TPB") + Bit(5, "TP") + Bit(4, 2, "BPB") + Bit(1, "WEL") + Bit(0, "BUSY") 281 | + Register("Status Register-2", 8) + Bit(7, "SUS") + Bit(6, "CMP") + Bit(5, 3, "LB") + Bit(1, "QE") + Bit(0, "SRP1") 282 | + Register("Status Register-3", 8) + Bit(7, "RFU") + Bit(6, 5, "W6:5") + Bit(4, "W4") + Bit(3, 0, "LC") 283 | + Cmd1(0x05, "RDSR1", "Read Status Register-1") + RegisterRead("Status Register-1") 284 | + Cmd1(0x50, "WRENVSR", "Write Enable for Volatile Status Register") 285 | + Cmd1(0x01, "WS1", "Write status registers") + RegisterWrite("Status Register-1") + RegisterWrite("Status Register-2") + RegisterWrite("Status Register-3") + 286 | + Cmd1(0x07, "RDSR2", "Read Status Register-2") + RegisterRead("Status Register-2") 287 | + Cmd1(0x35, "RDCR", "Read Configuration Register") + RegisterWrite("Status Register-2") 288 | + Cmd1(0x33, "RDSR3", "Read Status register-3") + RegisterRead("Status Register-3") 289 | + Cmd1(0x01, "WRR", "Write Status Registers") + RegisterWrite("Status Register-1") 290 | + Cmd1(0xB9, "BRAC", "Bank Register Access") 291 | + Cmd1(0x17, "BRWR", "Bank Register Write") 292 | + Cmd1(0x18, "ECCRD", "ECC Statuc Register Read") + ADDR + DummyBytes(1) + OP_DATA_READ 293 | + Cmd1(0x14, "ABRD", "Auto Boot Register Read") 294 | + Cmd1(0x14, "ABWR", "Auto Boot Register Write") 295 | + Cmd1(0x43, "PNVDLR", "Programm NVDLR") + OP_DATA_WRITE 296 | + Cmd1(0x4A, "WVDLR", "Write VDLR") + OP_DATA_WRITE 297 | + Cmd1(0x41, "DLPRD", "Data Learning Patter Read") + OP_DATA_READ 298 | + Cmd1(0x30, "CLSR", "Clear Status Register") 299 | + Cmd1(0x77, "Set Burst with Wrap") + QUAD_IO + DummyBytes(3) + OP_DATA_WRITE 300 | + Cmd1(0x39, "Set Block/Pointer protection") + ADDR 301 | + Cmd1(0x48, "Read Security Registers") + ADDR + DummyBytes(1) + OP_DATA_READ 302 | + Cmd1(0x44, "Erase Security Registers") + ADDR 303 | + Cmd1(0x42, "Program Security Registers") + ADDR + OP_DATA_WRITE 304 | 305 | + CommandSet(0x9D, "Issi", 0xEF) 306 | + Register("Function Register", 8) + Bit(7, "IRL3") + Bit(6, "IRL2") + Bit(5, "IRL1") + Bit(4, 2, "IRL0") + Bit(3, "ESUS") + Bit(2, "PSUS") 307 | + Cmd1(0x48, "Read Function Register") + RegisterRead("Function Register") 308 | + Cmd1(0x42, "Write Function Register") + RegisterWrite("Function Register") 309 | + Cmd14(0x68, "IRRD", "Read Information Row") + ADDR + DummyBytes(1) + OP_DATA_READ 310 | + Cmd14(0x62, "IRP", "Information Row Program") + ADDR + OP_DATA_WRITE 311 | + Cmd14(0x64, "IRER", "Erase Information Row") + ADDR 312 | + Cmd14(0x26, "SECUNLOCK", "Sector Unlock") + ADDR 313 | + Cmd14(0x24, "SECLOCK", "Sector Lock") + ADDR 314 | + Cmd14(0xD7, "SE", "SER", "Sector erase") + ADDR 315 | /* 0x38 Differes from Winbond */ 316 | + Cmd1(0x38, "QPP", "Quad Input Page Program") + QUAD_DATA + ADDR + OP_DATA_WRITE 317 | + Cmd1(0xB0, "SUSP", "Erase/Program Suspend") 318 | + Cmd1(0x30, "RESM", "Erase/Program Resume") 319 | + Cmd1(0x35, "*4", "QPIEN", "Enter QPI Mode") + SET_QUAD 320 | + Cmd4(0xF5, "*1", "QPIDI", "Exit QPI Mode") + SET_SINGLE 321 | 322 | + CommandSet(0x20, "Micron", 0) 323 | + Register("Nonvolatile Configuration Register", 16) + Bit(15, 12, "DCC") + Bit(11, 9, "XIPMODE") + Bit(8, 6, "ODS") + Bit(4, "Reset/Hold") + Bit(3, "QUAD") + Bit(2, "DUAL") 324 | + Register("Volatile Configuration Register", 8) + Bit(7, 4, "DCC") + Bit(3, "XIP") + Bit(1, 0, "Wrap") 325 | + Register("Enhanced Volatile Configuration Register", 8) + Bit(7, "QUAD") + Bit(6, "DUAL") + Bit(4, "Reset/Hold") + Bit(3, "VPPACC") + Bit(2, 0, "ODS") 326 | + Register("Flag Status Register", 8) + Bit(7, "RDY") + Bit(6, "Erase suspend") + Bit(5, "Erase fail") + Bit(4, "Program fail") + Bit(3, "VPP fail") + Bit(2, "Program suspend") + Bit(1, "Protection fail") 327 | + Register("Lock Register", 8) + Bit(1, "SLD") + Bit(0, "SWL") 328 | 329 | + Cmd24(0xAF, "Multiple I/O READ ID") /* TODO: */ 330 | + Cmd124(0x5A, "SFDP", "Read SFDP Register") + ADDR + DummyBytes(1) + OP_DATA_READ 331 | + Cmd124(0x0B, "R", "Fast Read") + ADDR + DummyBytes(1) + OP_DATA_READ 332 | + Cmd12(0x3B, "R", "R 1-1-2", "Fast Read Dual Ouput") + ADDR + DummyBytes(1) + DUAL_DATA + OP_DATA_READ 333 | + Cmd12(0xBB, "R", "R 1-2-2", "Fast Read Dual I/O") + DUAL_IO + ADDR + M + DummyBytes(1) + OP_DATA_READ 334 | + Cmd14(0x6B, "R", "R 1-1-4", "Fast Read Quad Output") + ADDR + DummyBytes(1) + QUAD_DATA + OP_DATA_READ 335 | + Cmd14(0xEB, "R", "R 1-4-4", "Fast Read Quad I/O") + QUAD_IO + ADDR + M + DummyBytes(2) + OP_DATA_READ 336 | 337 | + Cmd124(0x06, "WREN", "Write Enable") 338 | + Cmd124(0x04, "WRDI", "Write Disable") 339 | + Cmd124(0x05, "RDSR", "Read status register") + RegisterRead("Status Register-1") 340 | + Cmd124(0x01, "WS1", "Write status register") + RegisterWrite("Status Register-1") 341 | + Cmd124(0xE8, "RDLR", "Read lock register") + RegisterRead("Lock Register") 342 | + Cmd124(0xE5, "WRLR", "Write lock register") + RegisterWrite("Lock Register") 343 | + Cmd124(0x70, "RDFSR", "Read flag status register") + RegisterRead("Flag Status Register") 344 | + Cmd124(0x50, "WRFSR", "Write flag status register") + RegisterWrite("Flag Status Register") 345 | + Cmd124(0xB5, "RDNVCR", "Read nonvolatile configuration register") + RegisterRead("Nonvolatile Configuration Register") 346 | + Cmd124(0xB1, "WRNVCR", "Write nonvolatile configuration register") + RegisterWrite("Nonvolatile Configuration Register") 347 | + Cmd124(0x85, "RDVCR", "Read volatile configuration register") + RegisterRead("Volatile Configuration Register") 348 | + Cmd124(0x81, "WRVCR", "Write volatile configuration register") + RegisterWrite("Volatile Configuration Register") 349 | + Cmd124(0x85, "RDEVCR", "Read enhanced volatile configuration register") + RegisterRead("Enhanced Volatile Configuration Register") 350 | + Cmd124(0x81, "WREVCR", "Write enhanced volatile configuration register") + RegisterWrite("Enhanced Volatile Configuration Register") 351 | 352 | + Cmd124(0x02, "PP", "Page Program") + ADDR + OP_DATA_WRITE 353 | + Cmd12(0xA2, "DPP", "Dual Input Fast Program") + DUAL_DATA + ADDR + OP_DATA_WRITE 354 | + Cmd12(0xD2, "DPP", "Extended Dual Input Fast Program") + DUAL_IO + ADDR + OP_DATA_WRITE 355 | 356 | + Cmd14(0x32, "QPP", "Quad Input Fast program") + DUAL_DATA + ADDR + OP_DATA_WRITE 357 | + Cmd14(0x12, "QPP", "Extended Quad Input Fast Program") + DUAL_IO + ADDR + OP_DATA_WRITE 358 | 359 | + Cmd124(0x20, "SSE", "Subsector erase") + ADDR 360 | + Cmd124(0xD8, "SE", "Sector erase") + ADDR 361 | + Cmd124(0xC7, "BE", "Bulk erase") + ADDR 362 | + Cmd124(0x75, "SUSP", "Erase/Program Suspend") 363 | + Cmd124(0x7A, "RESM", "Erase/Program Resume") 364 | 365 | + Cmd124(0x75, "ROTP", "Read OTP Array") + OP_DATA_READ 366 | + Cmd124(0x7A, "POTP", "Program OTP Array") + OP_DATA_WRITE 367 | 368 | + CommandSet(0xBF, "Microchip", 0xEF) 369 | + Register("Status Register", 8) + Bit(7, "BUSY") + Bit(5, "SEC") + Bit(4, "WPLD") + Bit(3, "WSP") + Bit(2, "WSE") + Bit(1, "WEL") + Bit(0, "BUSY") 370 | + Register("Configuration Register", 8) + Bit(7, "WPEN") + Bit(3, "BPNV") + Bit(1, "IOC") 371 | + Cmd1(0x05, "RDSR", "Read status register") + RegisterRead("Status Register") 372 | + Cmd4(0x05, "RDSR", "Read status register") + DummyBytes(1) + RegisterRead("Status Register") 373 | + Cmd1(0x35, "RDCR", "Read configuration register") + RegisterWrite("Configuration Register") 374 | + Cmd4(0x35, "RDCR", "Read configuration register") + DummyBytes(1) + RegisterWrite("Configuration Register") 375 | + Cmd4(0x0B, "R", "Fast Read") + ADDR + DummyBytes(3) + OP_DATA_READ 376 | + Cmd1(0xEB, "R", "R 1-4-4", "Fast Read Quad I/O") + QUAD_IO + ADDR + M + DummyBytes(3) + OP_DATA_READ 377 | + Cmd14(0xC0, "SB", "Set Burst Length") + OP_DATA_WRITE 378 | + Cmd4(0x0C, "RBSQI", "Burst Read with Wrap") + ADDR + M + DummyBytes(3) + OP_DATA_READ 379 | + Cmd1(0xEC, "RBSPI", "Burst Read with Wrap") + ADDR + M + DummyBytes(3) + OP_DATA_READ 380 | + Cmd14(0xB0, "SUSP", "Erase/Program Suspend") 381 | + Cmd14(0x30, "RESM", "Erase/Program Resume") 382 | 383 | + Cmd1(0x72, "RBPR", "Read Block Protection Register") + OP_DATA_READ 384 | + Cmd4(0x72, "RBPR", "Read Block Protection Register") + DummyBytes(1) + OP_DATA_READ 385 | + Cmd14(0x8D, "LBPR", "Lock Down Block Protection Register") 386 | + Cmd14(0xE8, "nVWLDR", "Non-volatile Write Lock Down Register") + OP_DATA_WRITE 387 | + Cmd14(0x98, "ULBPR", "Global Block Protection Unlock") 388 | + Cmd1(0x88, "RSID", "Read Security ID") + ADDR2 + DummyBytes(1) + OP_DATA_READ 389 | + Cmd4(0x88, "RSID", "Read Security ID") + ADDR2 + DummyBytes(3) + OP_DATA_READ 390 | + Cmd14(0xA5, "PSID", "Program User Security ID Area") + ADDR2 + OP_DATA_WRITE 391 | + Cmd14(0x85, "LSID", "Lockout Security ID Programming") 392 | 393 | ; 394 | } 395 | 396 | SpiFlash::SpiFlash() : mSpiMode(SPI_MODE0), mDefBusMode(SINGLE), mCurBusMode(SINGLE), mCurrentCmd(nullptr), mActiveCmdSet(nullptr), mDataIn(false), mAddressBits(24) 397 | { 398 | addCommands(*this); 399 | } 400 | 401 | SpiFlash spiFlash; 402 | 403 | --------------------------------------------------------------------------------