-
Notifications
You must be signed in to change notification settings - Fork 25
feat: add pattern generator SCPI commands #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
IM-TechieScientist
wants to merge
5
commits into
fossasia:dev26
Choose a base branch
from
IM-TechieScientist:digital-pattern-generator-scpi
base: dev26
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
609ea52
feat: add pattern generator SCPI commands
IM-TechieScientist 8cbc7ae
fix: clk div and pg_init
IM-TechieScientist 3abb8ab
docs: add pattern generator commands
IM-TechieScientist 4ceee9b
feat: pack pattern generator samples
IM-TechieScientist 6465cf3
feat: track pattern generator underruns
IM-TechieScientist File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| #include "application/pattern_generator_commands.h" | ||
|
|
||
| #include <string.h> | ||
|
|
||
| #include "system/pattern_generator.h" | ||
|
|
||
| enum { | ||
| PG_DEFAULT_PIN_BASE = 16, | ||
| PG_DEFAULT_PIN_COUNT = 1, | ||
| PG_DEFAULT_RATE_HZ = 2000, | ||
| PG_MAX_PIN_COUNT = 8, | ||
| PG_MAX_PATTERN_WORDS = 16384, | ||
| PG_MAX_RATE_HZ = 75000000, | ||
| }; | ||
|
|
||
| static PatternGenerator pg; | ||
| static bool pg_initialized; | ||
| static uint32_t pattern_buffer[PG_MAX_PATTERN_WORDS]; | ||
|
|
||
| static struct { | ||
| uint32_t pin_base; | ||
| uint32_t pin_count; | ||
| uint32_t rate_hz; | ||
| uint32_t pattern_words; | ||
| PatternGeneratorMode mode; | ||
| } state = { | ||
| .pin_base = PG_DEFAULT_PIN_BASE, | ||
| .pin_count = PG_DEFAULT_PIN_COUNT, | ||
| .rate_hz = PG_DEFAULT_RATE_HZ, | ||
| .pattern_words = 0, | ||
| .mode = PATTERN_GENERATOR_MODE_ONCE, | ||
| }; | ||
|
|
||
| static bool config_is_valid(void) | ||
| { | ||
| return state.pin_count >= 1 && state.pin_count <= PG_MAX_PIN_COUNT && | ||
| state.pin_base + state.pin_count <= 30 && state.rate_hz >= 1 && | ||
| state.rate_hz <= PG_MAX_RATE_HZ; | ||
| } | ||
|
|
||
| static bool apply_config(void) | ||
| { | ||
| if (!config_is_valid()) { | ||
| return false; | ||
| } | ||
|
|
||
| PatternGeneratorConfig config = { | ||
| .pin_base = state.pin_base, | ||
| .pin_count = state.pin_count, | ||
| .rate_hz = state.rate_hz, | ||
| }; | ||
|
|
||
| if (pg_initialized) { | ||
| return pattern_generator_configure(&pg, &config); | ||
| } | ||
|
|
||
| pg_initialized = pattern_generator_init(&pg, &config); | ||
| return pg_initialized; | ||
| } | ||
|
|
||
| static void mark_unconfigured(void) | ||
| { | ||
| if (pg_initialized) { | ||
| pattern_generator_deinit(&pg); | ||
| } | ||
|
|
||
| pg_initialized = false; | ||
| } | ||
|
|
||
| void pg_reset_state(void) | ||
| { | ||
| pg_stop(); | ||
| if (pg_initialized) { | ||
| pattern_generator_deinit(&pg); | ||
| } | ||
|
|
||
| pg_initialized = false; | ||
| state.pin_base = PG_DEFAULT_PIN_BASE; | ||
| state.pin_count = PG_DEFAULT_PIN_COUNT; | ||
| state.rate_hz = PG_DEFAULT_RATE_HZ; | ||
| state.pattern_words = 0; | ||
| state.mode = PATTERN_GENERATOR_MODE_ONCE; | ||
| memset(pattern_buffer, 0, sizeof(pattern_buffer)); | ||
| } | ||
|
|
||
| void pg_task(void) | ||
| { | ||
| if (pg_initialized) { | ||
| pattern_generator_task(&pg); | ||
| } | ||
| } | ||
|
|
||
| bool pg_set_pins(uint32_t pin_base, uint32_t pin_count) | ||
| { | ||
| if (pin_count < 1 || pin_count > PG_MAX_PIN_COUNT || | ||
| pin_base + pin_count > 30 || pg_is_running()) { | ||
| return false; | ||
| } | ||
|
|
||
| uint32_t old_pin_base = state.pin_base; | ||
| uint32_t old_pin_count = state.pin_count; | ||
| state.pin_base = pin_base; | ||
| state.pin_count = pin_count; | ||
|
|
||
| if (pg_initialized && !apply_config()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have made changes to set it to false |
||
| state.pin_base = old_pin_base; | ||
| state.pin_count = old_pin_count; | ||
| mark_unconfigured(); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool pg_set_rate(uint32_t rate_hz) | ||
| { | ||
| if (rate_hz < 1 || rate_hz > PG_MAX_RATE_HZ || pg_is_running()) { | ||
| return false; | ||
| } | ||
|
|
||
| uint32_t old_rate_hz = state.rate_hz; | ||
| state.rate_hz = rate_hz; | ||
|
|
||
| if (pg_initialized && !apply_config()) { | ||
| state.rate_hz = old_rate_hz; | ||
| mark_unconfigured(); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool pg_set_mode_once(void) | ||
| { | ||
| if (pg_is_running()) { | ||
| return false; | ||
| } | ||
|
|
||
| state.mode = PATTERN_GENERATOR_MODE_ONCE; | ||
| return true; | ||
| } | ||
|
|
||
| bool pg_set_mode_loop(void) | ||
| { | ||
| if (pg_is_running()) { | ||
| return false; | ||
| } | ||
|
|
||
| state.mode = PATTERN_GENERATOR_MODE_LOOP; | ||
| return true; | ||
| } | ||
|
|
||
| bool pg_upload_data(uint8_t const *data, size_t len) | ||
| { | ||
| if (!data || len == 0 || (len % sizeof(uint32_t)) != 0 || pg_is_running()) { | ||
| return false; | ||
| } | ||
|
|
||
| size_t word_count = len / sizeof(uint32_t); | ||
| if (word_count > PG_MAX_PATTERN_WORDS) { | ||
| return false; | ||
| } | ||
|
|
||
| memcpy(pattern_buffer, data, len); | ||
| state.pattern_words = (uint32_t)word_count; | ||
| return true; | ||
| } | ||
|
|
||
| bool pg_start(void) | ||
| { | ||
| if (state.pattern_words == 0) { | ||
| return false; | ||
| } | ||
|
|
||
| if (!pg_initialized && !apply_config()) { | ||
| return false; | ||
| } | ||
|
|
||
| return pattern_generator_start( | ||
| &pg, | ||
| pattern_buffer, | ||
| state.pattern_words, | ||
| state.mode | ||
| ); | ||
| } | ||
|
|
||
| void pg_stop(void) | ||
| { | ||
| if (pg_initialized) { | ||
| pattern_generator_stop(&pg); | ||
| } | ||
| } | ||
|
|
||
| uint32_t pg_get_pin_base(void) { return state.pin_base; } | ||
|
|
||
| uint32_t pg_get_pin_count(void) { return state.pin_count; } | ||
|
|
||
| uint32_t pg_get_rate(void) { return state.rate_hz; } | ||
|
|
||
| bool pg_get_mode_loop(void) { return state.mode == PATTERN_GENERATOR_MODE_LOOP; } | ||
|
|
||
| uint32_t pg_get_pattern_words(void) { return state.pattern_words; } | ||
|
|
||
| uint32_t pg_get_underruns(void) | ||
| { | ||
| return pg_initialized ? pattern_generator_get_underruns(&pg) : 0; | ||
| } | ||
|
|
||
| bool pg_is_running(void) | ||
| { | ||
| return pg_initialized && pattern_generator_is_running(&pg); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| #ifndef PATTERN_GENERATOR_COMMANDS_H | ||
| #define PATTERN_GENERATOR_COMMANDS_H | ||
|
|
||
| #include <stdbool.h> | ||
| #include <stddef.h> | ||
| #include <stdint.h> | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| void pg_reset_state(void); | ||
| void pg_task(void); | ||
|
|
||
| bool pg_set_pins(uint32_t pin_base, uint32_t pin_count); | ||
| bool pg_set_rate(uint32_t rate_hz); | ||
| bool pg_set_mode_once(void); | ||
| bool pg_set_mode_loop(void); | ||
| bool pg_upload_data(uint8_t const *data, size_t len); | ||
| bool pg_start(void); | ||
| void pg_stop(void); | ||
|
|
||
| uint32_t pg_get_pin_base(void); | ||
| uint32_t pg_get_pin_count(void); | ||
| uint32_t pg_get_rate(void); | ||
| bool pg_get_mode_loop(void); | ||
| uint32_t pg_get_pattern_words(void); | ||
| uint32_t pg_get_underruns(void); | ||
| bool pg_is_running(void); | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Align configuration validation limits with low-level constraints to improve error consistency
config_is_validonly enforcesrate_hz <= PG_MAX_RATE_HZ, whilepattern_output_ll_configureadditionally requiresclk_div >= 1.0fbased on the actual peripheral clock. As a result, some rates that pass this check will still be rejected later with less clear SCPI errors.To make failures consistent and predictable, either derive
PG_MAX_RATE_HZfromPLATFORM_get_peripheral_clock_speed(or a documented worst-case), or replicate theclk_div >= 1constraint here so all invalid configs are rejected at the same layer with a clear, shared limit.Suggested implementation:
To keep this check perfectly aligned with
pattern_output_ll_configure, ensure that theclk_divcomputation here matches whatever that function uses (e.g., if it divides by additional factors such as prescalers or pattern word widths, mirror that logic instead of the simpleperipheral_clk_hz / state.rate_hzused above). IfPLATFORM_get_peripheral_clock_speedis already declared via another header in this translation unit, you can omit the additional#include "system/platform.h"to avoid redundant includes.