Counter-intuitively, this actually saves us two cycles because we unroll
the first iteration of the loop that spins on the interrupt flag, saving
a branch in the case that the flag is clear the first time.
* Initial commit of hackrf_biast
* Cleaned up hackrf_biast
* Cleaned up usage info
* Include getopt.h for non-GNU systems
* Add support for overriding HackRF's default antenna power behavior in firmware. Add support for specifying antenna power behavior in libhackrf.
* Moved bias tee config routines into user_config.c, cleaned up operation of hackrf_biast
* hackrf_biast now calls usage() and exits when invoked with no arguments
* Fixed minor documentation error in usage()
* minor syntax cleanup
* Add some documentation to the host API call
* Add proper declaration magic in hackrf.h to hackrf_set_user_bias_t_opts() to appease Visual Studio
* Documentation changes suggested by @martinling
* Moved bias t setting above switch statement, removed line that explicit turned bias t off when entering OFF mode
* Change hackrf_set_user_bias_t_opts() to use a friendly struct() instead of a bitmask. User friendliness fixes to hackrf_biast options. More clang-format appeasement.
* Removed support for integer mode args from hackrf_biast
* clang-format error fixes
* Tweaked position of comment for clang-format v14
* Reformat files with clang-format v14 instead of 16
* Remove internal numeric modes for bias T settings
Co-authored-by: Martin Ling <martin-github@earth.li>
* Fix documentation error in hackrf_biast.c
---------
Co-authored-by: Martin Ling <martin-github@earth.li>
During r9 hardware development it was thought that the MAX2839 would use
a different GPIO pin for chip select, but it ended up being the same pin
as is used for MAX2837 on other hardware revisions.
This takes the MAX283x abstraction a bit further and fixes a bug with
hackrf_debug -m.
The bootloader is configured by pin straps on certain pins. We use some
of those for other purposes in r9 which causes the bootloader to
misbehave if the device is reset from software. By switching these pins
from outputs to inputs just before reset this problem is avoided.
The rad1o was not starting the M0 when powered up by inserting a USB
cable. Interestingly the M0 does start when toggling the power switch.
Resetting the M0 before starting it in `main()` solves this issue.
Firmware now detects the hardware it is running on at startup and
refuses to run if it is compiled for the wrong platform. The board ID
returned by firmware to the host is now derived from run-time detection
rather than a compile-time value. A separate method to retrieve
compile-time supported platform is added.
On HackRF One, pin straps are checked to determine hardware revision.
This is informational to aid troubleshooting and does not affect any
function.
On rad1o, the UI update could block this loop from running for long
enough that it could stall in a state where neither of the conditions
was met.
Fix this by removing the 'phase' variable, in favour of a counter
tracking the number of bytes that have been scheduled for USB transfer.
Whenever there are enough bytes to schedule the next transfer, do so.
Meanwhile, the M0 count is prevented from wrapping around and clobbering
data not yet sent, because the M0 code monitors the m4_count variable
which is updated as each transfer completes.
This change avoids various possible races in which an autonomous mode
change by the M0 might clobber a mode change made from the M4, as well
as related races on other state fields that can be written by the M4.
The previous mode field is replaced by two separate ones:
- active_mode, which is written only by the M0, and indicates the
current operating mode.
- requested_mode, which is written by the M4 to request a change.
This field includes both the requested mode, and a flag bit. The M4
writes the field with the flag bit set, and must then wait for the
M0 to signal completion of the request by clearing the flag bit.
Whilst the M4 is blocked waiting for the flag bit to be cleared, the
M0 can safely make all the required changes to the state that are
needed for the transition to the requested mode. Once the transition
is complete, the M0 clears the flag bit and the M4 continues execution.
Request handling is implemented in the idle loop. To handle requests,
mode-specific loops simply need to check the request flag and branch to
idle if it is set.
A request from the M4 to change modes will always require passing
through the idle loop, and is not subject to timing guarantees. Only
transitions made autonomously by the M0 have guaranteed timing
constraints.
The work previously done in reset_counts is now implemented as part of
the request handling, so the tx_start, rx_start and wait_start labels
are no longer required.
An extra two cycles are required in the TX shortfall path because we
must now load the active mode to check whether we are in TX_START.
Two cycles are saved in the normal TX path because updating the active
mode to TX_RUN can now be done without checking the previous value.
Previously, finding the M0 in IDLE mode was ambiguous; it could indicate
either a normal outcome, or a shortfall limit having being hit.
To disambiguate, we add an error field to the M0 state. The errors
currently possible are an RX timeout or a TX timeout, both of which
can be obtained efficiently from the current operating mode due to
the values used.
This adds 3 cycles to both shortfall paths, in order to shift down
the mode to obtain the error code, and store it to the M0 state.
The previous implementation of sweep mode had the M0 continuing to
receive and buffer samples during retuning. To avoid using data affected
by retuning, the code discarded two 16K blocks of samples after
retuning, before transferring one 16K block to the host.
However, retuning has to be done with the USB IRQ masked. The M4 byte
count cannot be advanced by the bulk transfer completion callback whilst
retuning is ongoing. This makes an RX buffer overrun likely, and
overruns now stall the M0, causing sweep timing to become inconsistent.
It makes much more sense to stop the M0 receiving data during retuning.
Using scheduled M0 mode changes between the RX and WAIT modes, it's now
possible to do this whilst retaining consistent sweep timing. The
comment block added to the start of the `sleep_mode()` function explains
the new implementation.
The new scheme substantially reduces the timing constraints on the host
retrieving the data. Previously, the host had to retrieve each sample
block before the M0 overwrote it, which would occur midway through
retuning for the next sweep, with samples that were going to be
discarded anyway.
With the new scheme, buffer space is used efficiently. No data is
written to the buffer which will be discarded. The host does not need to
finish retrieving each 16K block until its buffer space is due to be
reused, which is not until two sweep steps later. A great deal more
jitter in the bulk transfer timing can therefore now be tolerated,
without affecting sweep timing.
If the host does delay the retrieval of a block enough that its buffer
space is about to be reused, the M0 now stalls. This in turn will stall
the M4 sweep loop, causing the sweep to be paused until there is enough
buffer space to continue. Previously, sweeping continued regardless, and
the host received corrupted data if it did not keep up.
This lays the groundwork for implementing timed operations (#86). The M0
can be configured to automatically change modes when its byte count
reaches a specific value.
Checking the counter against the threshold and dispatching to the next
mode is handled by a new `jump_next_mode` macro, which replaces the
unconditional branches back to the start of the TX and RX loops.
Making this change work requires some rearrangement of the code, such
that the destinations of all conditional branch instructions are within
reach. These branch instructions (`b[cond] label`) have a range of -256
to +254 bytes from the current program counter.
For this reason, the TX shortfall handling is moved earlier in the file,
and branches in the idle loop are restructured to use an unconditional
branch to rx_start, which is furthest away.
The additional code for switching modes adds 9 cycles to the normal RX
path, and 10 to the TX path (the difference is because the dispatch in
`jump_next_mode` is optimised for the longer RX path).
During shutdown of TX or RX, the host may stop supplying or retrieving
sample data some time before a stop request causes the M0 to be set back
to idle mode.
This makes it common for a spurious shortfall to occur during shutdown,
giving the misleading impression that there has been a throughput
problem. In fact, the final shortfall is simply an artifact.
This commit detects when this happens, and excludes the spurious
shortfall from the stats.
To implement this, we back up the shortfall stats whenever a new
shortfall begins. If the new shortfall later turns out to be spurious,
as indicated by a transition to IDLE while it is ongoing, then we roll
back the stats to their previous values.
We actually only need to back up previous longest shortfall length. To
get a previous shortfall count, can simply to subtract one from the
current shortfall count.
This change adds four cycles to the two shortfall paths - a load and
store to back up the previous longest shortfall length.
This count is only written by the M0, so there's no need to reload it
when the current value is already retained in a register.
Removing this load saves two cycles in all code paths.
Using our newly-defined macros, it's now straightforward to write
separate loops for RX and TX, with the idle loop dispatching to them
when a new mode setting is written by the M4.
This saves some cycles by reducing branches needed within each loop, and
makes it simpler to add new modes.
For macros which use internal labels, a name parameter is added. This
parameter is prefixed to the labels used, so that each mode's use of
that macro produces its own label names.
Similarly, where branches were taken in the handle_shortfall macro to
the "loop" label, these are replaced with the appropriate tx_loop or
rx_loop label.
The syntax `\name\()_suffix` is necessary to perform concatenation in
the GNU assembler.