Skip to content

Fix: Update SPI driver for firmware v3 by using _BURST commands#275

Open
TejasAnalyst wants to merge 4 commits intofossasia:mainfrom
TejasAnalyst:fix-spi-v3-driver
Open

Fix: Update SPI driver for firmware v3 by using _BURST commands#275
TejasAnalyst wants to merge 4 commits intofossasia:mainfrom
TejasAnalyst:fix-spi-v3-driver

Conversation

@TejasAnalyst
Copy link

@TejasAnalyst TejasAnalyst commented Feb 14, 2026

Description

This PR updates the SPI driver to be compatible with firmware v3.
The older firmware relied on START and STOP commands, which have been deprecated and removed in v3. This PR correctly replaces them with the new *_BURST commands.

Changes Made

  • Updated _TRANSFER_COMMANDS_MAP to use CP.SEND_SPI8_BURST and CP.SEND_SPI16_BURST.
  • Removed redundant self._start() and self._stop() calls from _transfer and transfer functions, as the burst commands now handle the complete transaction internally.

Fixes #262

Summary by Sourcery

Update SPI bus driver and related tests for compatibility with firmware v3 burst transfer commands.

Bug Fixes:

  • Switch SPI transfer command mapping to use burst commands compatible with firmware v3.
  • Remove redundant SPI START/STOP calls now handled by burst transfer commands.
  • Use fixed PWM frequency constants in SPI and UART tests where instance properties are not accessible.
  • Correct PWM frequency constant name in SPI and UART tests to avoid typos and inconsistencies.

Tests:

  • Adjust SPI tests to use a static PWM frequency value instead of accessing a non-existent class-level frequency attribute.
  • Adjust UART tests to use a static PWM frequency value instead of accessing an instance-only baudrate attribute.

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 14, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Updates the SPI driver to use firmware v3 *_BURST transfer commands and removes now-unnecessary START/STOP bracketing in SPI operations, while adjusting SPI/UART tests to use fixed PWM frequencies instead of class-level instance properties.

Sequence diagram for SPI transfer8 using firmware v3 burst command

sequenceDiagram
    participant Client
    participant SPI as _SPIPrimitive
    participant FirmwareV3

    Client->>SPI: transfer8(data)
    activate SPI
    SPI->>SPI: _transfer(data, 8)
    SPI->>FirmwareV3: SEND_SPI8_BURST(data)
    FirmwareV3-->>SPI: data_in
    SPI-->>Client: data_in
    deactivate SPI
Loading

Updated class diagram for SPI primitive with burst commands

classDiagram
    class _SPIPrimitive {
        -_TRANSFER_COMMANDS_MAP: dict
        -_INTEGER_TYPE_MAP: dict
        +transfer8(data: int) int
        +transfer16(data: int) int
        +transfer8_bulk(data: List[int]) List[int]
        +transfer16_bulk(data: List[int]) List[int]
        +read8() int
        +read16() int
        +read8_bulk(data_to_read: int) List[int]
        +read16_bulk(data_to_read: int) List[int]
        -_transfer(data: int, bits: int) int
        -_transfer_bulk(data: List[int], bits: int) List[int]
        -_read(bits: int) int
        -_read_bulk(data_to_read: int, bits: int) List[int]
    }
Loading

File-Level Changes

Change Details Files
Switch SPI transfer opcodes to firmware v3 burst commands and rely on them to encapsulate full transactions.
  • Update the SPI transfer command map to use CP.SEND_SPI8_BURST and CP.SEND_SPI16_BURST for 8- and 16-bit transfers
  • Remove explicit _start()/_stop() calls from single-word transfer, bulk transfer, and read helper methods that now use burst commands
pslab/bus/spi.py
Stabilize SPI and UART tests by using fixed PWM frequencies instead of accessing instance-only frequency/baudrate values via the class.
  • Replace PWM_FERQUENCY in SPI tests with a constant 100000.0 Hz and update all references and derived timing calculations to use PWM_FREQUENCY
  • Replace PWM_FERQUENCY in UART tests with a constant 500000.0 Hz and update PWM generation to use PWM_FREQUENCY
tests/test_spi.py
tests/test_uart.py

Assessment against linked issues

Issue Objective Addressed Explanation
#262 Update the SPI driver command mapping to use the new firmware v3 *_BURST SPI commands instead of the deprecated low-level START/STOP-based commands.
#262 Remove usage of low-level START/STOP operations in SPI transfer/read methods so that transactions rely solely on the *_BURST commands compatible with firmware v3.

Possibly linked issues

  • Update SPI driver for firmware v3 #262: PR replaces deprecated START/STOP with *_BURST commands, exactly updating the SPI driver for firmware v3 as requested.
  • #: PR touches tests/test_spi.py and tests/test_uart.py, fixing PWM_FERQUENCY typo and problematic frequency expressions from issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • If _start() and _stop() are no longer used anywhere after switching to the *_BURST commands, consider removing these methods (and any related state) to simplify the SPI driver and avoid dead code.
  • You now have hard-coded frequency literals in multiple places in the tests; consider centralizing these values (e.g., as module-level constants or a helper) so future changes to the test frequencies only need to be made in one place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- If `_start()` and `_stop()` are no longer used anywhere after switching to the `*_BURST` commands, consider removing these methods (and any related state) to simplify the SPI driver and avoid dead code.
- You now have hard-coded frequency literals in multiple places in the tests; consider centralizing these values (e.g., as module-level constants or a helper) so future changes to the test frequencies only need to be made in one place.

## Individual Comments

### Comment 1
<location> `tests/test_spi.py:34` </location>
<code_context>
 SPIMaster._secondary_prescaler = SPRE = 0
-PWM_FERQUENCY = SPIMaster._frequency * 2 / 3
+# Static value 100kHz used because instance property '_frequency' cannot be accessed on the class.
+PWM_FREQUENCY = 100000.0
 MICROSECONDS = 1e-6
 RELTOL = 0.05
</code_context>

<issue_to_address>
**suggestion (testing):** Avoid hardcoding PWM frequency; derive it from the SPI master instance to keep the test aligned with implementation changes.

Hardcoding `PWM_FREQUENCY = 100000.0` means the test may no longer reflect the real SPI clock if `_frequency` or its configuration changes. Instead, compute the PWM frequency in a fixture that has access to the `SPIMaster` instance (e.g., make `la` depend on a `spi_master` fixture and derive `pwm_frequency = spi_master._frequency * 2 / 3`, or use a public API if one exists) before calling `pwm.generate(...)`. This keeps the test coupled to the actual SPI settings and preserves its value as a regression test.

Suggested implementation:

```python
SPIMaster._primary_prescaler = PPRE = 0
SPIMaster._secondary_prescaler = SPRE = 0
MICROSECONDS = 1e-6
RELTOL = 0.05
# Number of expected logic level changes.
@pytest.fixture
def la(handler: SerialHandler, spi_master: SPIMaster) -> LogicAnalyzer:
    pwm = PWMGenerator(handler)
    pwm_frequency = spi_master._frequency * 2 / 3
    pwm.generate(SDI[1], pwm_frequency, 0.5)
    return LogicAnalyzer(handler)

```

If the SPI master fixture is not currently named `spi_master` or has a different type, update the `la` fixture's parameter list to match the existing fixture name and type. Also, if `_frequency` is not accessible or a public API exists (e.g. `get_frequency()`), replace `spi_master._frequency` with the appropriate public accessor to avoid depending on private attributes.
</issue_to_address>

### Comment 2
<location> `tests/test_uart.py:18-23` </location>
<code_context>
 RXD2 = "SQ1"
-PWM_FERQUENCY = UART._baudrate // 2
+# Static value 500kHz (half of default 1MHz baudrate) used as instance property cannot be accessed here.
+PWM_FREQUENCY = 500000.0
 MICROSECONDS = 1e-6
 RELTOL = 0.05
</code_context>

<issue_to_address>
**suggestion (testing):** Use the UART instance’s configured baudrate to derive PWM frequency instead of a hardcoded constant.

The old `PWM_FERQUENCY = UART._baudrate // 2` kept the PWM tied to the UART config; the new fixed `500000.0` will become wrong if the default baudrate changes. Consider deriving the PWM frequency from the `uart` fixture (e.g. `pwm_frequency = uart._baudrate / 2` or via a public accessor) and passing that into `pwm.generate(...)` so this integration test continues to validate the actual UART behavior rather than a hardcoded assumption.

```suggestion
RXD2 = "SQ1"
MICROSECONDS = 1e-6
RELTOL = 0.05
# Number of expected logic level changes.
@pytest.fixture
def pwm(handler: SerialHandler, uart: UART) -> None:
    pwm_frequency = uart._baudrate / 2.0
    pwm = PWMGenerator(handler)
    pwm.generate(RXD2, pwm_frequency, 0.5)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

SPIMaster._secondary_prescaler = SPRE = 0
PWM_FERQUENCY = SPIMaster._frequency * 2 / 3
# Static value 100kHz used because instance property '_frequency' cannot be accessed on the class.
PWM_FREQUENCY = 100000.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Avoid hardcoding PWM frequency; derive it from the SPI master instance to keep the test aligned with implementation changes.

Hardcoding PWM_FREQUENCY = 100000.0 means the test may no longer reflect the real SPI clock if _frequency or its configuration changes. Instead, compute the PWM frequency in a fixture that has access to the SPIMaster instance (e.g., make la depend on a spi_master fixture and derive pwm_frequency = spi_master._frequency * 2 / 3, or use a public API if one exists) before calling pwm.generate(...). This keeps the test coupled to the actual SPI settings and preserves its value as a regression test.

Suggested implementation:

SPIMaster._primary_prescaler = PPRE = 0
SPIMaster._secondary_prescaler = SPRE = 0
MICROSECONDS = 1e-6
RELTOL = 0.05
# Number of expected logic level changes.
@pytest.fixture
def la(handler: SerialHandler, spi_master: SPIMaster) -> LogicAnalyzer:
    pwm = PWMGenerator(handler)
    pwm_frequency = spi_master._frequency * 2 / 3
    pwm.generate(SDI[1], pwm_frequency, 0.5)
    return LogicAnalyzer(handler)

If the SPI master fixture is not currently named spi_master or has a different type, update the la fixture's parameter list to match the existing fixture name and type. Also, if _frequency is not accessible or a public API exists (e.g. get_frequency()), replace spi_master._frequency with the appropriate public accessor to avoid depending on private attributes.

Comment on lines 18 to 23
RXD2 = "SQ1"
PWM_FERQUENCY = UART._baudrate // 2
# Static value 500kHz (half of default 1MHz baudrate) used as instance property cannot be accessed here.
PWM_FREQUENCY = 500000.0
MICROSECONDS = 1e-6
RELTOL = 0.05
# Number of expected logic level changes.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Use the UART instance’s configured baudrate to derive PWM frequency instead of a hardcoded constant.

The old PWM_FERQUENCY = UART._baudrate // 2 kept the PWM tied to the UART config; the new fixed 500000.0 will become wrong if the default baudrate changes. Consider deriving the PWM frequency from the uart fixture (e.g. pwm_frequency = uart._baudrate / 2 or via a public accessor) and passing that into pwm.generate(...) so this integration test continues to validate the actual UART behavior rather than a hardcoded assumption.

Suggested change
RXD2 = "SQ1"
PWM_FERQUENCY = UART._baudrate // 2
# Static value 500kHz (half of default 1MHz baudrate) used as instance property cannot be accessed here.
PWM_FREQUENCY = 500000.0
MICROSECONDS = 1e-6
RELTOL = 0.05
# Number of expected logic level changes.
RXD2 = "SQ1"
MICROSECONDS = 1e-6
RELTOL = 0.05
# Number of expected logic level changes.
@pytest.fixture
def pwm(handler: SerialHandler, uart: UART) -> None:
pwm_frequency = uart._baudrate / 2.0
pwm = PWMGenerator(handler)
pwm.generate(RXD2, pwm_frequency, 0.5)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update SPI driver for firmware v3

2 participants