├── .gitignore ├── CMakeLists.txt ├── .clang-format ├── src ├── I2cAnalyzerSettings.h ├── I2cAnalyzerResults.h ├── I2cSimulationDataGenerator.h ├── I2cAnalyzer.h ├── I2cAnalyzerSettings.cpp ├── I2cSimulationDataGenerator.cpp ├── I2cAnalyzerResults.cpp └── I2cAnalyzer.cpp ├── LICENSE ├── cmake └── ExternalAnalyzerSDK.cmake ├── .github └── workflows │ └── build.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.11) 2 | project(i2c_analyzer) 3 | 4 | add_definitions( -DLOGIC2 ) 5 | 6 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum supported MacOS version" FORCE) 7 | 8 | # enable generation of compile_commands.json, helpful for IDEs to locate include files. 9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 10 | 11 | # custom CMake Modules are located in the cmake directory. 12 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 13 | 14 | include(ExternalAnalyzerSDK) 15 | 16 | set(SOURCES 17 | src/I2cAnalyzer.cpp 18 | src/I2cAnalyzer.h 19 | src/I2cAnalyzerResults.cpp 20 | src/I2cAnalyzerResults.h 21 | src/I2cAnalyzerSettings.cpp 22 | src/I2cAnalyzerSettings.h 23 | src/I2cSimulationDataGenerator.cpp 24 | src/I2cSimulationDataGenerator.h 25 | ) 26 | 27 | add_analyzer_plugin(i2c_analyzer SOURCES ${SOURCES}) 28 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Logic style 2 | Language: Cpp 3 | # manually added flags 4 | FixNamespaceComments: 'false' 5 | SortIncludes: 'false' 6 | 7 | 8 | # flags copied from web editor, https://zed0.co.uk/clang-format-configurator/ 9 | AllowShortBlocksOnASingleLine: 'false' 10 | AllowShortCaseLabelsOnASingleLine: 'false' 11 | AllowShortFunctionsOnASingleLine: None 12 | AllowShortIfStatementsOnASingleLine: 'false' 13 | AllowShortLoopsOnASingleLine: 'false' 14 | AlwaysBreakAfterReturnType: None 15 | AlwaysBreakTemplateDeclarations: 'true' 16 | BreakBeforeBraces: Allman 17 | ColumnLimit: '140' 18 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' 19 | ContinuationIndentWidth: '4' 20 | Cpp11BracedListStyle: 'false' 21 | IndentWidth: '4' 22 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 23 | MaxEmptyLinesToKeep: '2' 24 | NamespaceIndentation: All 25 | PointerAlignment: Left 26 | SpaceBeforeParens: Never 27 | SpaceInEmptyParentheses: 'false' 28 | SpacesInCStyleCastParentheses: 'true' 29 | SpacesInParentheses: 'true' 30 | SpacesInSquareBrackets: 'true' 31 | TabWidth: '4' 32 | UseTab: Never 33 | -------------------------------------------------------------------------------- /src/I2cAnalyzerSettings.h: -------------------------------------------------------------------------------- 1 | #ifndef I2C_ANALYZER_SETTINGS 2 | #define I2C_ANALYZER_SETTINGS 3 | 4 | #include 5 | #include 6 | 7 | enum I2cDirection 8 | { 9 | I2C_READ, 10 | I2C_WRITE 11 | }; 12 | enum I2cResponse 13 | { 14 | I2C_ACK, 15 | I2C_NAK 16 | }; 17 | 18 | enum AddressDisplay 19 | { 20 | NO_DIRECTION_7, 21 | NO_DIRECTION_8, 22 | YES_DIRECTION_8 23 | }; 24 | 25 | class I2cAnalyzerSettings : public AnalyzerSettings 26 | { 27 | public: 28 | I2cAnalyzerSettings(); 29 | virtual ~I2cAnalyzerSettings(); 30 | 31 | virtual bool SetSettingsFromInterfaces(); 32 | virtual void LoadSettings( const char* settings ); 33 | virtual const char* SaveSettings(); 34 | 35 | void UpdateInterfacesFromSettings(); 36 | 37 | Channel mSdaChannel; 38 | Channel mSclChannel; 39 | 40 | protected: 41 | std::auto_ptr mSdaChannelInterface; 42 | std::auto_ptr mSclChannelInterface; 43 | }; 44 | 45 | #endif // I2C_ANALYZER_SETTINGS 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Saleae 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 | -------------------------------------------------------------------------------- /src/I2cAnalyzerResults.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_ANALYZER_RESULTS 2 | #define SERIAL_ANALYZER_RESULTS 3 | 4 | #include 5 | 6 | #define I2C_FLAG_ACK ( 1 << 0 ) 7 | #define I2C_MISSING_FLAG_ACK ( 1 << 1 ) 8 | 9 | enum I2cFrameType 10 | { 11 | I2cAddress, 12 | I2cData 13 | }; 14 | 15 | class I2cAnalyzer; 16 | class I2cAnalyzerSettings; 17 | 18 | class I2cAnalyzerResults : public AnalyzerResults 19 | { 20 | public: 21 | I2cAnalyzerResults( I2cAnalyzer* analyzer, I2cAnalyzerSettings* settings ); 22 | virtual ~I2cAnalyzerResults(); 23 | 24 | virtual void GenerateBubbleText( U64 frame_index, Channel& channel, DisplayBase display_base ); 25 | virtual void GenerateExportFile( const char* file, DisplayBase display_base, U32 export_type_user_id ); 26 | 27 | virtual void GenerateFrameTabularText( U64 frame_index, DisplayBase display_base ); 28 | virtual void GeneratePacketTabularText( U64 packet_id, DisplayBase display_base ); 29 | virtual void GenerateTransactionTabularText( U64 transaction_id, DisplayBase display_base ); 30 | 31 | protected: // functions 32 | protected: // vars 33 | I2cAnalyzerSettings* mSettings; 34 | I2cAnalyzer* mAnalyzer; 35 | }; 36 | 37 | #endif // SERIAL_ANALYZER_RESULTS 38 | -------------------------------------------------------------------------------- /src/I2cSimulationDataGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_SIMULATION_DATA_GENERATOR 2 | #define SERIAL_SIMULATION_DATA_GENERATOR 3 | 4 | #include 5 | #include "I2cAnalyzerSettings.h" 6 | #include 7 | 8 | class I2cSimulationDataGenerator 9 | { 10 | public: 11 | I2cSimulationDataGenerator(); 12 | ~I2cSimulationDataGenerator(); 13 | 14 | void Initialize( U32 simulation_sample_rate, I2cAnalyzerSettings* settings ); 15 | U32 GenerateSimulationData( U64 newest_sample_requested, U32 sample_rate, SimulationChannelDescriptor** simulation_channels ); 16 | 17 | protected: 18 | I2cAnalyzerSettings* mSettings; 19 | U32 mSimulationSampleRateHz; 20 | U8 mValue; 21 | 22 | protected: // I2c specific 23 | // functions 24 | void CreateI2cTransaction( U8 address, I2cDirection direction, U8 data ); 25 | void CreateI2cByte( U8 data, I2cResponse reply ); 26 | void CreateBit( BitState bit_state ); 27 | void CreateStart(); 28 | void CreateStop(); 29 | void CreateRestart(); 30 | void SafeChangeSda( BitState bit_state ); 31 | 32 | protected: // vars 33 | ClockGenerator mClockGenerator; 34 | 35 | SimulationChannelDescriptorGroup mI2cSimulationChannels; 36 | SimulationChannelDescriptor* mSda; 37 | SimulationChannelDescriptor* mScl; 38 | }; 39 | #endif // UNIO_SIMULATION_DATA_GENERATOR 40 | -------------------------------------------------------------------------------- /src/I2cAnalyzer.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_ANALYZER_H 2 | #define SERIAL_ANALYZER_H 3 | 4 | #include 5 | #include "I2cAnalyzerResults.h" 6 | #include "I2cSimulationDataGenerator.h" 7 | 8 | class I2cAnalyzerSettings; 9 | class I2cAnalyzer : public Analyzer2 10 | { 11 | public: 12 | I2cAnalyzer(); 13 | virtual ~I2cAnalyzer(); 14 | virtual void SetupResults(); 15 | virtual void WorkerThread(); 16 | 17 | virtual U32 GenerateSimulationData( U64 newest_sample_requested, U32 sample_rate, SimulationChannelDescriptor** simulation_channels ); 18 | virtual U32 GetMinimumSampleRateHz(); 19 | 20 | virtual const char* GetAnalyzerName() const; 21 | virtual bool NeedsRerun(); 22 | 23 | 24 | #pragma warning( push ) 25 | #pragma warning( \ 26 | disable : 4251 ) // warning C4251: 'SerialAnalyzer::<...>' : class <...> needs to have dll-interface to be used by clients of class 27 | 28 | protected: // functions 29 | void AdvanceToStartBit(); 30 | void GetByte(); 31 | bool GetBit( BitState& bit_state, U64& sck_rising_edge ); 32 | bool GetBitPartOne( BitState& bit_state, U64& sck_rising_edge, U64& frame_end_sample ); 33 | bool GetBitPartTwo(); 34 | void RecordStartStopBit(); 35 | 36 | protected: // vars 37 | std::auto_ptr mSettings; 38 | std::auto_ptr mResults; 39 | AnalyzerChannelData* mSda; 40 | AnalyzerChannelData* mScl; 41 | 42 | I2cSimulationDataGenerator mSimulationDataGenerator; 43 | bool mSimulationInitilized; 44 | 45 | // Serial analysis vars: 46 | U32 mSampleRateHz; 47 | bool mNeedAddress; 48 | std::vector mArrowLocations; 49 | 50 | #pragma warning( pop ) 51 | }; 52 | 53 | extern "C" ANALYZER_EXPORT const char* __cdecl GetAnalyzerName(); 54 | extern "C" ANALYZER_EXPORT Analyzer* __cdecl CreateAnalyzer(); 55 | extern "C" ANALYZER_EXPORT void __cdecl DestroyAnalyzer( Analyzer* analyzer ); 56 | 57 | #endif // SERIAL_ANALYZER_H 58 | -------------------------------------------------------------------------------- /cmake/ExternalAnalyzerSDK.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | # Use the C++11 standard 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | set(CMAKE_CXX_STANDARD_REQUIRED YES) 7 | 8 | if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY OR NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin/) 10 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin/) 11 | endif() 12 | 13 | # Fetch the Analyzer SDK if the target does not already exist. 14 | if(NOT TARGET Saleae::AnalyzerSDK) 15 | FetchContent_Declare( 16 | analyzersdk 17 | GIT_REPOSITORY https://github.com/saleae/AnalyzerSDK.git 18 | GIT_TAG master 19 | GIT_SHALLOW True 20 | GIT_PROGRESS True 21 | ) 22 | 23 | FetchContent_GetProperties(analyzersdk) 24 | 25 | if(NOT analyzersdk_POPULATED) 26 | FetchContent_Populate(analyzersdk) 27 | include(${analyzersdk_SOURCE_DIR}/AnalyzerSDKConfig.cmake) 28 | 29 | if(APPLE OR WIN32) 30 | get_target_property(analyzersdk_lib_location Saleae::AnalyzerSDK IMPORTED_LOCATION) 31 | if(CMAKE_LIBRARY_OUTPUT_DIRECTORY) 32 | file(COPY ${analyzersdk_lib_location} DESTINATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 33 | else() 34 | message(WARNING "Please define CMAKE_RUNTIME_OUTPUT_DIRECTORY and CMAKE_LIBRARY_OUTPUT_DIRECTORY if you want unit tests to locate ${analyzersdk_lib_location}") 35 | endif() 36 | endif() 37 | 38 | endif() 39 | endif() 40 | 41 | function(add_analyzer_plugin TARGET) 42 | set(options ) 43 | set(single_value_args ) 44 | set(multi_value_args SOURCES) 45 | cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}" ${ARGN} ) 46 | 47 | 48 | add_library(${TARGET} MODULE ${_p_SOURCES}) 49 | target_link_libraries(${TARGET} PRIVATE Saleae::AnalyzerSDK) 50 | 51 | set(ANALYZER_DESTINATION "Analyzers") 52 | install(TARGETS ${TARGET} RUNTIME DESTINATION ${ANALYZER_DESTINATION} 53 | LIBRARY DESTINATION ${ANALYZER_DESTINATION}) 54 | 55 | set_target_properties(${TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${ANALYZER_DESTINATION} 56 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${ANALYZER_DESTINATION}) 57 | endfunction() -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: 7 | - '*' 8 | pull_request: 9 | branches: [master] 10 | 11 | jobs: 12 | windows: 13 | runs-on: windows-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Build 17 | run: | 18 | cmake -B ${{github.workspace}}/build -A x64 19 | cmake --build ${{github.workspace}}/build --config Release 20 | - name: Upload windows build 21 | uses: actions/upload-artifact@v4 22 | with: 23 | name: windows 24 | path: ${{github.workspace}}/build/Analyzers/Release/*.dll 25 | macos: 26 | runs-on: macos-latest 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Build 30 | run: | 31 | cmake -B ${{github.workspace}}/build/x86_64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 32 | cmake --build ${{github.workspace}}/build/x86_64 33 | cmake -B ${{github.workspace}}/build/arm64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 34 | cmake --build ${{github.workspace}}/build/arm64 35 | - name: Upload MacOS x86_64 build 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: macos_x86_64 39 | path: ${{github.workspace}}/build/x86_64/Analyzers/*.so 40 | - name: Upload MacOS arm64 build 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: macos_arm64 44 | path: ${{github.workspace}}/build/arm64/Analyzers/*.so 45 | linux: 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v4 49 | - name: Pull pre-built builder image 50 | run: docker pull ghcr.io/saleae/analyzer-build-image:latest 51 | - name: Build inside container 52 | run: | 53 | docker run --rm \ 54 | -v ${{ github.workspace }}:/app \ 55 | -w /app \ 56 | ghcr.io/saleae/analyzer-build-image:latest \ 57 | bash -c 'cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build' 58 | - name: Upload Linux build 59 | uses: actions/upload-artifact@v4 60 | with: 61 | name: linux 62 | path: ${{github.workspace}}/build/Analyzers/*.so 63 | publish: 64 | needs: [windows, macos, linux] 65 | runs-on: ubuntu-latest 66 | steps: 67 | - name: download individual builds 68 | uses: actions/download-artifact@v4 69 | with: 70 | path: ${{github.workspace}}/artifacts 71 | - name: zip 72 | run: | 73 | cd ${{github.workspace}}/artifacts 74 | zip -r ${{github.workspace}}/analyzer.zip . 75 | - uses: actions/upload-artifact@v4 76 | with: 77 | name: all-platforms 78 | path: ${{github.workspace}}/artifacts/** 79 | - name: create release 80 | uses: softprops/action-gh-release@v1 81 | if: startsWith(github.ref, 'refs/tags/') 82 | with: 83 | files: ${{github.workspace}}/analyzer.zip -------------------------------------------------------------------------------- /src/I2cAnalyzerSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "I2cAnalyzerSettings.h" 2 | 3 | #include 4 | #include 5 | 6 | I2cAnalyzerSettings::I2cAnalyzerSettings() : mSdaChannel( UNDEFINED_CHANNEL ), mSclChannel( UNDEFINED_CHANNEL ) 7 | { 8 | mSdaChannelInterface.reset( new AnalyzerSettingInterfaceChannel() ); 9 | mSdaChannelInterface->SetTitleAndTooltip( "SDA", "Serial Data Line" ); 10 | mSdaChannelInterface->SetChannel( mSdaChannel ); 11 | 12 | mSclChannelInterface.reset( new AnalyzerSettingInterfaceChannel() ); 13 | mSclChannelInterface->SetTitleAndTooltip( "SCL", "Serial Clock Line" ); 14 | mSclChannelInterface->SetChannel( mSclChannel ); 15 | 16 | AddInterface( mSdaChannelInterface.get() ); 17 | AddInterface( mSclChannelInterface.get() ); 18 | 19 | // AddExportOption( 0, "Export as text/csv file", "text (*.txt);;csv (*.csv)" ); 20 | AddExportOption( 0, "Export as text/csv file" ); 21 | AddExportExtension( 0, "text", "txt" ); 22 | AddExportExtension( 0, "csv", "csv" ); 23 | 24 | ClearChannels(); 25 | AddChannel( mSdaChannel, "SDA", false ); 26 | AddChannel( mSclChannel, "SCL", false ); 27 | } 28 | 29 | I2cAnalyzerSettings::~I2cAnalyzerSettings() 30 | { 31 | } 32 | 33 | bool I2cAnalyzerSettings::SetSettingsFromInterfaces() 34 | { 35 | if( mSdaChannelInterface->GetChannel() == mSclChannelInterface->GetChannel() ) 36 | { 37 | SetErrorText( "SDA and SCL can't be assigned to the same input." ); 38 | return false; 39 | } 40 | 41 | mSdaChannel = mSdaChannelInterface->GetChannel(); 42 | mSclChannel = mSclChannelInterface->GetChannel(); 43 | 44 | ClearChannels(); 45 | AddChannel( mSdaChannel, "SDA", true ); 46 | AddChannel( mSclChannel, "SCL", true ); 47 | 48 | return true; 49 | } 50 | 51 | void I2cAnalyzerSettings::LoadSettings( const char* settings ) 52 | { 53 | SimpleArchive text_archive; 54 | text_archive.SetString( settings ); 55 | 56 | const char* name_string; // the first thing in the archive is the name of the protocol analyzer that the data belongs to. 57 | text_archive >> &name_string; 58 | if( strcmp( name_string, "SaleaeI2CAnalyzer" ) != 0 ) 59 | AnalyzerHelpers::Assert( "SaleaeI2CAnalyzer: Provided with a settings string that doesn't belong to us;" ); 60 | 61 | text_archive >> mSdaChannel; 62 | text_archive >> mSclChannel; 63 | 64 | ClearChannels(); 65 | AddChannel( mSdaChannel, "SDA", true ); 66 | AddChannel( mSclChannel, "SCL", true ); 67 | 68 | UpdateInterfacesFromSettings(); 69 | } 70 | 71 | const char* I2cAnalyzerSettings::SaveSettings() 72 | { 73 | SimpleArchive text_archive; 74 | 75 | text_archive << "SaleaeI2CAnalyzer"; 76 | text_archive << mSdaChannel; 77 | text_archive << mSclChannel; 78 | 79 | return SetReturnString( text_archive.GetString() ); 80 | } 81 | 82 | void I2cAnalyzerSettings::UpdateInterfacesFromSettings() 83 | { 84 | mSdaChannelInterface->SetChannel( mSdaChannel ); 85 | mSclChannelInterface->SetChannel( mSclChannel ); 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Saleae I2C Analyzer 2 | 3 | Saleae I2C Analyzer 4 | 5 | ## Getting Started 6 | 7 | The following documentation describes how to build this analyzer locally. For more detailed information about the Analyzer SDK, debugging, CI builds, and more, check out the readme in the Sample Analyzer repository. 8 | 9 | https://github.com/saleae/SampleAnalyzer 10 | 11 | ### MacOS 12 | 13 | Dependencies: 14 | 15 | - XCode with command line tools 16 | - CMake 3.13+ 17 | - git 18 | 19 | Install command line tools after XCode is installed: 20 | 21 | ``` 22 | xcode-select --install 23 | ``` 24 | 25 | Then open XCode, open Preferences from the main menu, go to locations, and select the only option under 'Command line tools'. 26 | 27 | Install CMake on MacOS: 28 | 29 | 1. Download the binary distribution for MacOS, `cmake-*-Darwin-x86_64.dmg` 30 | 2. Install the usual way by dragging into applications. 31 | 3. Open a terminal and run the following: 32 | 33 | ``` 34 | /Applications/CMake.app/Contents/bin/cmake-gui --install 35 | ``` 36 | 37 | _Note: Errors may occur if older versions of CMake are installed._ 38 | 39 | Build the analyzer: 40 | 41 | ``` 42 | mkdir build 43 | cd build 44 | cmake .. 45 | cmake --build . 46 | ``` 47 | 48 | ### Ubuntu 18.04+ 49 | 50 | Dependencies: 51 | 52 | - CMake 3.13+ 53 | - gcc 4.8+ 54 | - git 55 | 56 | Misc dependencies: 57 | 58 | ``` 59 | sudo apt-get install build-essential 60 | ``` 61 | 62 | Build the analyzer: 63 | 64 | ``` 65 | mkdir build 66 | cd build 67 | cmake .. 68 | cmake --build . 69 | ``` 70 | 71 | ### Windows 72 | 73 | Dependencies: 74 | 75 | - Visual Studio 2019 76 | - CMake 3.13+ 77 | - git 78 | 79 | **Visual Studio 2019** 80 | 81 | _Note - newer and older versions of Visual Studio are likely to work._ 82 | 83 | Setup options: 84 | 85 | - Workloads > Desktop & Mobile > "Desktop development with C++" 86 | 87 | Note - if CMake has any problems with the MSVC compiler, it's likely a component is missing. 88 | 89 | **CMake** 90 | 91 | Download and install the latest CMake release here. 92 | https://cmake.org/download/ 93 | 94 | **git** 95 | 96 | Download and install git here. 97 | https://git-scm.com/ 98 | 99 | Build the analyzer: 100 | 101 | ``` 102 | mkdir build 103 | cd build 104 | cmake .. -A x64 105 | ``` 106 | 107 | Then, open the newly created solution file located here: `build\i2c_analyzer.sln` 108 | 109 | Optionally, build from the command line without opening Visual Studio: 110 | 111 | ``` 112 | cmake --build . 113 | ``` 114 | 115 | The built analyzer DLLs will be located here: 116 | 117 | `build\Analyzers\Debug` 118 | 119 | `build\Analyzers\Release` 120 | 121 | For debug and release builds, respectively. 122 | 123 | 124 | ## Output Frame Format 125 | 126 | ### Frame Type: `"address"` 127 | 128 | | Property | Type | Description | 129 | | :--- | :--- | :--- | 130 | | `address` | bytes | The 7 bit I2C address | 131 | | `read` | bool | True for read operations, false for write operations | 132 | | `ack` | bool | True when the address was ACKed, false when NAKed | 133 | | `error` | str | (optional) Present if an there was a problem reading the I2C data | 134 | 135 | I2C address byte 136 | 137 | ### Frame Type: `"data"` 138 | 139 | | Property | Type | Description | 140 | | :--- | :--- | :--- | 141 | | `data` | bytes | 8 bit data word | 142 | | `ack` | bool | True when the data byte was ACKed, false when NAKed | 143 | | `error` | str | (optional) Present if an there was a problem reading the I2C data | 144 | 145 | I2C data byte 146 | 147 | ### Frame Type: `"start"` 148 | 149 | | Property | Type | Description | 150 | | :--- | :--- | :--- | 151 | 152 | 153 | I2C start condition 154 | 155 | ### Frame Type: `"stop"` 156 | 157 | | Property | Type | Description | 158 | | :--- | :--- | :--- | 159 | 160 | 161 | I2C stop condition 162 | 163 | -------------------------------------------------------------------------------- /src/I2cSimulationDataGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "I2cSimulationDataGenerator.h" 2 | 3 | 4 | I2cSimulationDataGenerator::I2cSimulationDataGenerator() 5 | { 6 | } 7 | 8 | I2cSimulationDataGenerator::~I2cSimulationDataGenerator() 9 | { 10 | } 11 | 12 | void I2cSimulationDataGenerator::Initialize( U32 simulation_sample_rate, I2cAnalyzerSettings* settings ) 13 | { 14 | mSimulationSampleRateHz = simulation_sample_rate; 15 | mSettings = settings; 16 | 17 | mClockGenerator.Init( 400000, simulation_sample_rate ); 18 | 19 | mSda = mI2cSimulationChannels.Add( settings->mSdaChannel, mSimulationSampleRateHz, BIT_HIGH ); 20 | mScl = mI2cSimulationChannels.Add( settings->mSclChannel, mSimulationSampleRateHz, BIT_HIGH ); 21 | 22 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 10.0 ) ); // insert 10 bit-periods of idle 23 | 24 | mValue = 0; 25 | } 26 | 27 | U32 I2cSimulationDataGenerator::GenerateSimulationData( U64 largest_sample_requested, U32 sample_rate, 28 | SimulationChannelDescriptor** simulation_channels ) 29 | { 30 | U64 adjusted_largest_sample_requested = 31 | AnalyzerHelpers::AdjustSimulationTargetSample( largest_sample_requested, sample_rate, mSimulationSampleRateHz ); 32 | 33 | while( mScl->GetCurrentSampleNumber() < adjusted_largest_sample_requested ) 34 | { 35 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 500 ) ); 36 | 37 | 38 | if( rand() % 20 == 0 ) 39 | { 40 | CreateStart(); 41 | CreateI2cByte( 0x24, I2C_NAK ); 42 | CreateStop(); 43 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 80 ) ); 44 | } 45 | 46 | 47 | CreateI2cTransaction( 0xA0, I2C_WRITE, mValue++ + 12 ); 48 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 80 ) ); 49 | CreateI2cTransaction( 0xA0, I2C_READ, mValue++ - 43 + ( rand() % 100 ) ); 50 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 50 ) ); 51 | CreateI2cTransaction( 0x24, I2C_READ, mValue++ + ( rand() % 100 ) ); 52 | 53 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 2000 ) ); // insert 20 bit-periods of idle 54 | 55 | CreateI2cTransaction( 0x24, I2C_READ, mValue++ + 16 + ( rand() % 100 ) ); 56 | 57 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 100 ) ); 58 | } 59 | 60 | *simulation_channels = mI2cSimulationChannels.GetArray(); 61 | return mI2cSimulationChannels.GetCount(); 62 | } 63 | 64 | 65 | void I2cSimulationDataGenerator::CreateI2cTransaction( U8 address, I2cDirection direction, U8 data ) 66 | { 67 | U8 command = address << 1; 68 | if( direction == I2C_READ ) 69 | command |= 0x1; 70 | 71 | CreateStart(); 72 | CreateI2cByte( command, I2C_ACK ); 73 | CreateI2cByte( data, I2C_ACK ); 74 | CreateI2cByte( data, I2C_NAK ); 75 | CreateStop(); 76 | } 77 | 78 | void I2cSimulationDataGenerator::CreateI2cByte( U8 data, I2cResponse reply ) 79 | { 80 | if( mScl->GetCurrentBitState() == BIT_HIGH ) 81 | { 82 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 83 | mScl->Transition(); 84 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 85 | } 86 | 87 | BitExtractor bit_extractor( data, AnalyzerEnums::MsbFirst, 8 ); 88 | 89 | for( U32 i = 0; i < 8; i++ ) 90 | { 91 | CreateBit( bit_extractor.GetNextBit() ); 92 | } 93 | 94 | if( reply == I2C_ACK ) 95 | CreateBit( BIT_LOW ); 96 | else 97 | CreateBit( BIT_HIGH ); 98 | 99 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 4.0 ) ); 100 | } 101 | 102 | void I2cSimulationDataGenerator::CreateBit( BitState bit_state ) 103 | { 104 | if( mScl->GetCurrentBitState() != BIT_LOW ) 105 | AnalyzerHelpers::Assert( "CreateBit expects to be entered with scl low" ); 106 | 107 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 0.5 ) ); 108 | 109 | mSda->TransitionIfNeeded( bit_state ); 110 | 111 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 0.5 ) ); 112 | 113 | mScl->Transition(); // posedge 114 | 115 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 116 | 117 | mScl->Transition(); // negedge 118 | } 119 | 120 | void I2cSimulationDataGenerator::CreateStart() 121 | { 122 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 123 | 124 | // 1st, we need to make SDA high, 125 | SafeChangeSda( BIT_HIGH ); 126 | 127 | // 2nd, we need make the clock high. 128 | if( mScl->GetCurrentBitState() == BIT_LOW ) 129 | { 130 | mScl->Transition(); 131 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 132 | } 133 | 134 | // 3rd, bring SDA high. 135 | mSda->Transition(); 136 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 137 | } 138 | 139 | 140 | void I2cSimulationDataGenerator::CreateStop() 141 | { 142 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 143 | 144 | // 1st, we need to make SDA low, 145 | SafeChangeSda( BIT_LOW ); 146 | 147 | // 2nd, we need make the clock high. 148 | if( mScl->GetCurrentBitState() == BIT_LOW ) 149 | { 150 | mScl->Transition(); 151 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 152 | } 153 | 154 | // 3rd, bring SDA high. 155 | mSda->Transition(); 156 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 157 | } 158 | 159 | void I2cSimulationDataGenerator::CreateRestart() 160 | { 161 | CreateStart(); 162 | } 163 | 164 | void I2cSimulationDataGenerator::SafeChangeSda( BitState bit_state ) 165 | { 166 | if( mSda->GetCurrentBitState() != bit_state ) 167 | { 168 | // make sure SCK is low before we toggle it 169 | if( mScl->GetCurrentBitState() == BIT_HIGH ) 170 | { 171 | mScl->Transition(); 172 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 173 | } 174 | 175 | mSda->Transition(); 176 | mI2cSimulationChannels.AdvanceAll( mClockGenerator.AdvanceByHalfPeriod( 1.0 ) ); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/I2cAnalyzerResults.cpp: -------------------------------------------------------------------------------- 1 | #include "I2cAnalyzerResults.h" 2 | #include 3 | #include "I2cAnalyzer.h" 4 | #include "I2cAnalyzerSettings.h" 5 | #include 6 | #include 7 | #include 8 | 9 | I2cAnalyzerResults::I2cAnalyzerResults( I2cAnalyzer* analyzer, I2cAnalyzerSettings* settings ) 10 | : AnalyzerResults(), mSettings( settings ), mAnalyzer( analyzer ) 11 | { 12 | } 13 | 14 | I2cAnalyzerResults::~I2cAnalyzerResults() 15 | { 16 | } 17 | 18 | void I2cAnalyzerResults::GenerateBubbleText( U64 frame_index, Channel& /*channel*/, 19 | DisplayBase display_base ) // unrefereced vars commented out to remove warnings. 20 | { 21 | // we only need to pay attention to 'channel' if we're making bubbles for more than one channel (as set by 22 | // AddChannelBubblesWillAppearOn) 23 | ClearResultStrings(); 24 | Frame frame = GetFrame( frame_index ); 25 | 26 | char ack[ 32 ]; 27 | if( ( frame.mFlags & I2C_FLAG_ACK ) != 0 ) 28 | snprintf( ack, sizeof( ack ), "ACK" ); 29 | else if( ( frame.mFlags & I2C_MISSING_FLAG_ACK ) != 0 ) 30 | snprintf( ack, sizeof( ack ), "Missing ACK/NAK" ); 31 | else 32 | snprintf( ack, sizeof( ack ), "NAK" ); 33 | 34 | if( frame.mType == I2cAddress ) 35 | { 36 | char number_str[ 128 ]; 37 | AnalyzerHelpers::GetNumberString( frame.mData1 >> 1, display_base, 7, number_str, 128 ); 38 | 39 | I2cDirection direction; 40 | if( ( frame.mData1 & 0x1 ) != 0 ) 41 | direction = I2C_READ; 42 | else 43 | direction = I2C_WRITE; 44 | 45 | if( direction == I2C_READ ) 46 | { 47 | std::stringstream ss; 48 | AddResultString( "R" ); 49 | 50 | ss << "R[" << number_str << "]"; 51 | AddResultString( ss.str().c_str() ); 52 | ss.str( "" ); 53 | 54 | ss << "Read [" << number_str << "]"; 55 | AddResultString( ss.str().c_str() ); 56 | ss.str( "" ); 57 | 58 | ss << "Read [" << number_str << "] + " << ack; 59 | AddResultString( ss.str().c_str() ); 60 | ss.str( "" ); 61 | 62 | ss << "Setup Read to [" << number_str << "] + " << ack; 63 | AddResultString( ss.str().c_str() ); 64 | } 65 | else 66 | { 67 | std::stringstream ss; 68 | ss << "W[" << number_str << "]"; 69 | AddResultString( ss.str().c_str() ); 70 | ss.str( "" ); 71 | 72 | ss << "Write [" << number_str << "]"; 73 | AddResultString( ss.str().c_str() ); 74 | ss.str( "" ); 75 | 76 | ss << "Write [" << number_str << "] + " << ack; 77 | AddResultString( ss.str().c_str() ); 78 | ss.str( "" ); 79 | 80 | ss << "Setup Write to [" << number_str << "] + " << ack; 81 | AddResultString( ss.str().c_str() ); 82 | } 83 | } 84 | else 85 | { 86 | char number_str[ 128 ]; 87 | AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, number_str, 128 ); 88 | 89 | AddResultString( number_str ); 90 | 91 | std::stringstream ss; 92 | ss << number_str << " + " << ack; 93 | AddResultString( ss.str().c_str() ); 94 | } 95 | } 96 | 97 | void I2cAnalyzerResults::GenerateExportFile( const char* file, DisplayBase display_base, U32 /*export_type_user_id*/ ) 98 | { 99 | // export_type_user_id is only important if we have more than one export type. 100 | 101 | std::stringstream ss; 102 | void* f = AnalyzerHelpers::StartFile( file ); 103 | ; 104 | 105 | U64 trigger_sample = mAnalyzer->GetTriggerSample(); 106 | U32 sample_rate = mAnalyzer->GetSampleRate(); 107 | 108 | ss << "Time [s],Packet ID,Address,Data,Read/Write,ACK/NAK" << std::endl; 109 | 110 | char address[ 128 ] = ""; 111 | char rw[ 128 ] = ""; 112 | U64 num_frames = GetNumFrames(); 113 | for( U32 i = 0; i < num_frames; i++ ) 114 | { 115 | Frame frame = GetFrame( i ); 116 | 117 | if( frame.mType == I2cAddress ) 118 | { 119 | AnalyzerHelpers::GetNumberString( frame.mData1 >> 1, display_base, 7, address, 128 ); 120 | if( ( frame.mData1 & 0x1 ) != 0 ) 121 | snprintf( rw, sizeof( rw ), "Read" ); 122 | else 123 | snprintf( rw, sizeof( rw ), "Write" ); 124 | 125 | // check to see if the address packet is NAKed. If it is, we need to export the line here. 126 | if( ( frame.mFlags & I2C_FLAG_ACK ) == 0 ) 127 | { 128 | char ack[ 32 ]; 129 | if( ( frame.mFlags & I2C_MISSING_FLAG_ACK ) != 0 ) 130 | snprintf( ack, sizeof( ack ), "Missing ACK/NAK" ); 131 | else 132 | snprintf( ack, sizeof( ack ), "NAK" ); 133 | // we need to write out the line here. 134 | char time[ 128 ]; 135 | AnalyzerHelpers::GetTimeString( frame.mStartingSampleInclusive, trigger_sample, sample_rate, time, 128 ); 136 | 137 | ss << time << ",," << address << "," 138 | << "" 139 | << "," << rw << "," << ack << std::endl; 140 | AnalyzerHelpers::AppendToFile( ( U8* )ss.str().c_str(), ss.str().length(), f ); 141 | ss.str( std::string() ); 142 | } 143 | } 144 | else 145 | { 146 | char time[ 128 ]; 147 | AnalyzerHelpers::GetTimeString( frame.mStartingSampleInclusive, trigger_sample, sample_rate, time, 128 ); 148 | 149 | char data[ 128 ]; 150 | AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, data, 128 ); 151 | 152 | char ack[ 32 ]; 153 | if( ( frame.mFlags & I2C_FLAG_ACK ) != 0 ) 154 | snprintf( ack, sizeof( ack ), "ACK" ); 155 | else if( ( frame.mFlags & I2C_MISSING_FLAG_ACK ) != 0 ) 156 | snprintf( ack, sizeof( ack ), "Missing ACK/NAK" ); 157 | else 158 | snprintf( ack, sizeof( ack ), "NAK" ); 159 | 160 | 161 | U64 packet_id = GetPacketContainingFrameSequential( i ); 162 | if( packet_id != INVALID_RESULT_INDEX ) 163 | ss << time << "," << packet_id << "," << address << "," << data << "," << rw << "," << ack << std::endl; 164 | else 165 | ss << time << ",," << address << "," << data << "," << rw << "," << ack << std::endl; 166 | } 167 | 168 | AnalyzerHelpers::AppendToFile( ( U8* )ss.str().c_str(), ss.str().length(), f ); 169 | ss.str( std::string() ); 170 | 171 | 172 | if( UpdateExportProgressAndCheckForCancel( i, num_frames ) == true ) 173 | { 174 | AnalyzerHelpers::EndFile( f ); 175 | return; 176 | } 177 | } 178 | 179 | UpdateExportProgressAndCheckForCancel( num_frames, num_frames ); 180 | AnalyzerHelpers::EndFile( f ); 181 | } 182 | 183 | void I2cAnalyzerResults::GenerateFrameTabularText( U64 frame_index, DisplayBase display_base ) 184 | { 185 | ClearTabularText(); 186 | 187 | Frame frame = GetFrame( frame_index ); 188 | 189 | char ack[ 32 ]; 190 | if( ( frame.mFlags & I2C_FLAG_ACK ) != 0 ) 191 | snprintf( ack, sizeof( ack ), "ACK" ); 192 | else if( ( frame.mFlags & I2C_MISSING_FLAG_ACK ) != 0 ) 193 | snprintf( ack, sizeof( ack ), "Missing ACK/NAK" ); 194 | else 195 | snprintf( ack, sizeof( ack ), "NAK" ); 196 | 197 | if( frame.mType == I2cAddress ) 198 | { 199 | char number_str[ 128 ]; 200 | AnalyzerHelpers::GetNumberString( frame.mData1 >> 1, display_base, 7, number_str, 128 ); 201 | 202 | I2cDirection direction; 203 | if( ( frame.mData1 & 0x1 ) != 0 ) 204 | direction = I2C_READ; 205 | else 206 | direction = I2C_WRITE; 207 | 208 | if( direction == I2C_READ ) 209 | { 210 | std::stringstream ss; 211 | ss << "Setup Read to [" << number_str << "] + " << ack; 212 | AddTabularText( ss.str().c_str() ); 213 | } 214 | else 215 | { 216 | std::stringstream ss; 217 | ss << "Setup Write to [" << number_str << "] + " << ack; 218 | AddTabularText( ss.str().c_str() ); 219 | } 220 | } 221 | else 222 | { 223 | char number_str[ 128 ]; 224 | AnalyzerHelpers::GetNumberString( frame.mData1, display_base, 8, number_str, 128 ); 225 | std::stringstream ss; 226 | ss << number_str << " + " << ack; 227 | AddTabularText( ss.str().c_str() ); 228 | } 229 | } 230 | 231 | void I2cAnalyzerResults::GeneratePacketTabularText( U64 /*packet_id*/, 232 | DisplayBase /*display_base*/ ) // unrefereced vars commented out to remove warnings. 233 | { 234 | ClearResultStrings(); 235 | AddResultString( "not supported" ); 236 | } 237 | 238 | void 239 | I2cAnalyzerResults::GenerateTransactionTabularText( U64 /*transaction_id*/, 240 | DisplayBase /*display_base*/ ) // unrefereced vars commented out to remove warnings. 241 | { 242 | ClearResultStrings(); 243 | AddResultString( "not supported" ); 244 | } 245 | -------------------------------------------------------------------------------- /src/I2cAnalyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "I2cAnalyzer.h" 2 | #include "I2cAnalyzerSettings.h" 3 | #include 4 | 5 | I2cAnalyzer::I2cAnalyzer() : Analyzer2(), mSettings( new I2cAnalyzerSettings() ), mSimulationInitilized( false ) 6 | { 7 | SetAnalyzerSettings( mSettings.get() ); 8 | UseFrameV2(); 9 | } 10 | 11 | I2cAnalyzer::~I2cAnalyzer() 12 | { 13 | KillThread(); 14 | } 15 | 16 | void I2cAnalyzer::SetupResults() 17 | { 18 | mResults.reset( new I2cAnalyzerResults( this, mSettings.get() ) ); 19 | SetAnalyzerResults( mResults.get() ); 20 | mResults->AddChannelBubblesWillAppearOn( mSettings->mSdaChannel ); 21 | } 22 | 23 | void I2cAnalyzer::WorkerThread() 24 | { 25 | mSampleRateHz = GetSampleRate(); 26 | mNeedAddress = true; 27 | 28 | mSda = GetAnalyzerChannelData( mSettings->mSdaChannel ); 29 | mScl = GetAnalyzerChannelData( mSettings->mSclChannel ); 30 | 31 | AdvanceToStartBit(); 32 | mScl->AdvanceToNextEdge(); // now scl is low. 33 | 34 | for( ;; ) 35 | { 36 | GetByte(); 37 | CheckIfThreadShouldExit(); 38 | } 39 | } 40 | 41 | void I2cAnalyzer::GetByte() 42 | { 43 | mArrowLocations.clear(); 44 | U64 value; 45 | DataBuilder byte; 46 | byte.Reset( &value, AnalyzerEnums::MsbFirst, 8 ); 47 | U64 starting_sample = 0; 48 | U64 potential_ending_sample = 0; 49 | 50 | for( U32 i = 0; i < 8; i++ ) 51 | { 52 | BitState bit_state; 53 | U64 scl_rising_edge; 54 | bool result = GetBitPartOne( bit_state, scl_rising_edge, potential_ending_sample ); 55 | result &= GetBitPartTwo(); 56 | if( result == true ) 57 | { 58 | mArrowLocations.push_back( scl_rising_edge ); 59 | byte.AddBit( bit_state ); 60 | 61 | if( i == 0 ) 62 | starting_sample = scl_rising_edge; 63 | } 64 | else 65 | { 66 | return; 67 | } 68 | } 69 | 70 | BitState ack_bit_state; 71 | U64 scl_rising_edge; 72 | S64 last_valid_sample = mScl->GetSampleNumber(); 73 | bool result = GetBitPartOne( ack_bit_state, scl_rising_edge, potential_ending_sample ); // GetBit( ack_bit_state, scl_rising_edge ); 74 | 75 | FrameV2 framev2; 76 | char* framev2Type = nullptr; 77 | 78 | Frame frame; 79 | frame.mStartingSampleInclusive = starting_sample; 80 | frame.mEndingSampleInclusive = result ? potential_ending_sample : last_valid_sample; 81 | frame.mData1 = U8( value ); 82 | 83 | if( !result ) 84 | { 85 | framev2.AddString( "error", "missing ack/nak" ); 86 | frame.mFlags = I2C_MISSING_FLAG_ACK; 87 | } 88 | else 89 | { 90 | bool ack = ack_bit_state == BIT_LOW; 91 | 92 | // true == ack, false == nak 93 | framev2.AddBoolean( "ack", ack ); 94 | if( ack ) 95 | { 96 | frame.mFlags = I2C_FLAG_ACK; 97 | } 98 | } 99 | 100 | if( mNeedAddress == true && result == true ) // if result is false, then we have already recorded a stop bit and toggled mNeedAddress 101 | { 102 | mNeedAddress = false; 103 | bool is_read = value & 0x01; 104 | U8 address = value >> 1; 105 | frame.mType = I2cAddress; 106 | framev2Type = "address"; 107 | framev2.AddByte( "address", address ); 108 | framev2.AddBoolean( "read", is_read ); 109 | } 110 | else 111 | { 112 | frame.mType = I2cData; 113 | framev2Type = "data"; 114 | framev2.AddByte( "data", value ); 115 | } 116 | 117 | mResults->AddFrame( frame ); 118 | mResults->AddFrameV2( framev2, framev2Type, starting_sample, result ? potential_ending_sample : last_valid_sample ); 119 | 120 | U32 count = mArrowLocations.size(); 121 | for( U32 i = 0; i < count; i++ ) 122 | { 123 | mResults->AddMarker( mArrowLocations[ i ], AnalyzerResults::UpArrow, mSettings->mSclChannel ); 124 | } 125 | 126 | mResults->CommitResults(); 127 | 128 | result &= GetBitPartTwo(); 129 | } 130 | 131 | bool I2cAnalyzer::GetBit( BitState& bit_state, U64& sck_rising_edge ) 132 | { 133 | // SCL must be low coming into this function 134 | mScl->AdvanceToNextEdge(); // posedge 135 | sck_rising_edge = mScl->GetSampleNumber(); 136 | mSda->AdvanceToAbsPosition( sck_rising_edge ); // data read on SCL posedge 137 | 138 | bit_state = mSda->GetBitState(); 139 | bool result = true; 140 | 141 | // this while loop is only important if you need to be careful and check for things that that might happen at the very end of a data 142 | // set, and you don't want to get stuck waithing on a channel that never changes. 143 | while( mScl->DoMoreTransitionsExistInCurrentData() == false ) 144 | { 145 | // there are no more SCL transtitions, at least yet. 146 | if( mSda->DoMoreTransitionsExistInCurrentData() == true ) 147 | { 148 | // there ARE some SDA transtions, let's double check to make sure there's still no SDA activity 149 | auto next_data_edge = mSda->GetSampleOfNextEdge(); 150 | if( mScl->WouldAdvancingToAbsPositionCauseTransition( next_data_edge - 1 ) ) 151 | { 152 | break; 153 | } 154 | 155 | // ok, for sure we can advance to the next SDA edge without running past any SCL events. 156 | mSda->AdvanceToNextEdge(); 157 | 158 | RecordStartStopBit(); 159 | result = false; 160 | } 161 | } 162 | 163 | mScl->AdvanceToNextEdge(); // negedge; we'll leave the clock here 164 | while( mSda->WouldAdvancingToAbsPositionCauseTransition( mScl->GetSampleNumber() - 1 ) == true ) 165 | { 166 | // clock is high -- SDA changes indicate start, stop, etc. 167 | mSda->AdvanceToNextEdge(); 168 | RecordStartStopBit(); 169 | result = false; 170 | } 171 | 172 | return result; 173 | } 174 | 175 | bool I2cAnalyzer::GetBitPartOne( BitState& bit_state, U64& sck_rising_edge, U64& frame_end_sample ) 176 | { 177 | // SCL must be low coming into this function 178 | mScl->AdvanceToNextEdge(); // posedge 179 | sck_rising_edge = mScl->GetSampleNumber(); 180 | frame_end_sample = sck_rising_edge; 181 | mSda->AdvanceToAbsPosition( sck_rising_edge ); // data read on SCL posedge 182 | 183 | bit_state = mSda->GetBitState(); 184 | 185 | // clock is on the rising edge, and data is at the same location. 186 | 187 | while( mScl->DoMoreTransitionsExistInCurrentData() == false ) 188 | { 189 | // there are no more SCL transtitions, at least yet. 190 | if( mSda->DoMoreTransitionsExistInCurrentData() == true ) 191 | { 192 | // there ARE some SDA transtions, let's double check to make sure there's still no SDA activity 193 | auto next_data_edge = mSda->GetSampleOfNextEdge(); 194 | if( mScl->WouldAdvancingToAbsPositionCauseTransition( next_data_edge - 1 ) ) 195 | { 196 | break; 197 | } 198 | 199 | // ok, for sure we can advance to the next SDA edge without running past any SCL events. 200 | mSda->AdvanceToNextEdge(); 201 | mScl->AdvanceToAbsPosition( mSda->GetSampleNumber() ); // clock is still high, we're just moving it to the stop condition here. 202 | RecordStartStopBit(); 203 | return false; 204 | } 205 | } 206 | 207 | // ok, so there are more transitions on the clock channel, so the above code path didn't run. 208 | U64 sample_of_next_clock_falling_edge = mScl->GetSampleOfNextEdge(); 209 | while( mSda->WouldAdvancingToAbsPositionCauseTransition( sample_of_next_clock_falling_edge - 1 ) == true ) 210 | { 211 | // clock is high -- SDA changes indicate start, stop, etc. 212 | mSda->AdvanceToNextEdge(); 213 | mScl->AdvanceToAbsPosition( mSda->GetSampleNumber() ); // advance the clock to match the SDA channel. 214 | RecordStartStopBit(); 215 | return false; 216 | } 217 | 218 | if( mScl->DoMoreTransitionsExistInCurrentData() == true ) 219 | { 220 | frame_end_sample = mScl->GetSampleOfNextEdge(); 221 | } 222 | 223 | return true; 224 | } 225 | 226 | bool I2cAnalyzer::GetBitPartTwo() 227 | { 228 | // the sda and scl should be synced up, and we are either on a stop/start condition (clock high) or we're on a regular bit( clock high). 229 | // we also should not expect any more start/stop conditions before the next falling edge, I beleive. 230 | 231 | // move to next falling edge. 232 | bool result = true; 233 | mScl->AdvanceToNextEdge(); 234 | while( mSda->WouldAdvancingToAbsPositionCauseTransition( mScl->GetSampleNumber() - 1 ) == true ) 235 | { 236 | // clock is high -- SDA changes indicate start, stop, etc. 237 | mSda->AdvanceToNextEdge(); 238 | RecordStartStopBit(); 239 | result = false; 240 | } 241 | return result; 242 | } 243 | 244 | void I2cAnalyzer::RecordStartStopBit() 245 | { 246 | bool start = mSda->GetBitState() == BIT_LOW; 247 | if( start ) 248 | { 249 | // negedge -> START / restart 250 | mResults->AddMarker( mSda->GetSampleNumber(), AnalyzerResults::Start, mSettings->mSdaChannel ); 251 | 252 | FrameV2 framev2; 253 | mResults->AddFrameV2( framev2, "start", mSda->GetSampleNumber(), mSda->GetSampleNumber() + 1 ); 254 | } 255 | else 256 | { 257 | // posedge -> STOP 258 | mResults->AddMarker( mSda->GetSampleNumber(), AnalyzerResults::Stop, mSettings->mSdaChannel ); 259 | } 260 | 261 | mNeedAddress = true; 262 | mResults->CommitPacketAndStartNewPacket(); 263 | mResults->CommitResults(); 264 | 265 | if( !start ) 266 | { 267 | FrameV2 framev2; 268 | mResults->AddFrameV2( framev2, "stop", mSda->GetSampleNumber(), mSda->GetSampleNumber() + 1 ); 269 | } 270 | } 271 | 272 | void I2cAnalyzer::AdvanceToStartBit() 273 | { 274 | for( ;; ) 275 | { 276 | mSda->AdvanceToNextEdge(); 277 | 278 | if( mSda->GetBitState() == BIT_LOW ) 279 | { 280 | // SDA negedge 281 | mScl->AdvanceToAbsPosition( mSda->GetSampleNumber() ); 282 | if( mScl->GetBitState() == BIT_HIGH ) 283 | break; 284 | } 285 | } 286 | 287 | RecordStartStopBit(); 288 | } 289 | 290 | bool I2cAnalyzer::NeedsRerun() 291 | { 292 | return false; 293 | } 294 | 295 | U32 I2cAnalyzer::GenerateSimulationData( U64 minimum_sample_index, U32 device_sample_rate, 296 | SimulationChannelDescriptor** simulation_channels ) 297 | { 298 | if( mSimulationInitilized == false ) 299 | { 300 | mSimulationDataGenerator.Initialize( GetSimulationSampleRate(), mSettings.get() ); 301 | mSimulationInitilized = true; 302 | } 303 | 304 | return mSimulationDataGenerator.GenerateSimulationData( minimum_sample_index, device_sample_rate, simulation_channels ); 305 | } 306 | 307 | U32 I2cAnalyzer::GetMinimumSampleRateHz() 308 | { 309 | return 2000000; 310 | } 311 | 312 | const char* I2cAnalyzer::GetAnalyzerName() const 313 | { 314 | return "I2C"; 315 | } 316 | 317 | const char* GetAnalyzerName() 318 | { 319 | return "I2C"; 320 | } 321 | 322 | Analyzer* CreateAnalyzer() 323 | { 324 | return new I2cAnalyzer(); 325 | } 326 | 327 | void DestroyAnalyzer( Analyzer* analyzer ) 328 | { 329 | delete analyzer; 330 | } 331 | --------------------------------------------------------------------------------