Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9675741
Add mcu clock enable for revision R9 boards
fabriziop Feb 21, 2025
ce1b400
Parameterize clock source and multiplier for clock max speed
fabriziop Feb 21, 2025
9509f0e
Parameterize clock source and multiplier for cpu clock init
fabriziop Feb 21, 2025
965573c
Adapt MCU input clock routing to board revision
fabriziop Feb 21, 2025
e16b85e
Enable MCU timer3 peripheral
fabriziop Feb 21, 2025
3e9798d
Configure sampling trigger output
fabriziop Feb 21, 2025
52ad3fb
Configure PPS output
fabriziop Feb 21, 2025
1a87915
Add time timer init and isr
fabriziop Feb 21, 2025
5d95831
Add ticks per second divisor setting
fabriziop Feb 21, 2025
a828c8b
Add sampling trigger delay setting
fabriziop Feb 21, 2025
eda4ebf
Add seconds counter get/set
fabriziop Feb 21, 2025
dd45892
Add ticks counter get/set
fabriziop Feb 21, 2025
0007cf3
Add si5351c support for synchronized clock
fabriziop Feb 21, 2025
ccaf314
Add synchronized clock enabling
fabriziop Feb 21, 2025
96ae883
Promote required USB API version to 1.09
fabriziop Feb 21, 2025
8a7338c
Add hackrf time tool
fabriziop Feb 21, 2025
f705b2f
Rebasing, add HTime extension to compilation files
fabriziop Oct 14, 2025
9e90c12
Add HTime's documentation
fabriziop Feb 24, 2025
9e2adb5
Add watchdog on USB communication
fabriziop Apr 16, 2025
e8599df
Remove watchdog on USB communication: missing setting of
fabriziop May 12, 2025
1625b6f
Fix swapped order of SCU defs of PPS1 and TRIGGER
fabriziop Jun 15, 2025
afaece3
Generate trailing edges of 1PPS and trigger by counter
fabriziop Jun 16, 2025
03b3092
Fix the sampling hang with the external clock
fabriziop Jul 29, 2025
a6a00e7
Align documentation version to current 0.3.1
fabriziop Jul 30, 2025
cb18f35
Fix not R9 boards text description of clock jumper
fabriziop Oct 14, 2025
a34d15e
Improved clang-format version extraction in reformat script
fabriziop Oct 14, 2025
14aabd6
Formatted with hackrf clang 14 style
fabriziop Oct 14, 2025
e46ae96
Remove old bool typedef no more supported
fabriziop Dec 6, 2025
f441fda
- Add trigger hold
fabriziop Apr 24, 2026
c62d6b6
Fix clear pending int to proper bit of MR0
fabriziop Apr 25, 2026
3f5a812
- Add input PPS synchronization
fabriziop Apr 27, 2026
8ce4e1e
- Note on R9 boards that are better for sync
fabriziop May 20, 2026
806a493
Move HTime documentation from ReadTheDocs to local
fabriziop May 20, 2026
b48d8f3
Wrap some comments to 80 cols
fabriziop Jul 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ build:

# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/source/conf.py
configuration: docs_htime/source/conf.py

# Build PDF for docs
formats:
Expand Down
53 changes: 29 additions & 24 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
# HackRF
# HTime

This repository contains hardware designs and software for HackRF,
a low cost, open source Software Defined Radio platform.
HTime is a firmware extension for the HackRF One, the open-hardware/open-source
SDR by [Great Scott Gadgets](https://www.greatscottgadgets.com/hackrf/one/).
HTime provides the HackRF SDR with the
capability to perform timed commands and a precise phase synchronization
of internal clocks (sampling, MCU, output) for the best timing performance.
HTime is developed by Fabrizio Pollastri.

![HackRF One](https://raw.github.com/mossmann/hackrf/master/docs/images/HackRF-One-fd0-0009.jpeg)
## Features

(photo by fd0 from https://github.com/fd0/hackrf-one-pictures)
* Unix-like time scale, with a resolution of 5 ns
* RX/TX sampling synchronized with the time scale
* Fine time scale/frequency adjust for synchronization with remote reference
radio signals with a resolution of about 0.25 Hz @ 10 MHz
* No external hardware required
* Host time tool for access to time extension API

principal author: Michael Ossmann <mike@ossmann.com>
For now, HTime works only with a 10 MHz sample rate.

Information on HackRF and purchasing HackRF: https://greatscottgadgets.com/hackrf/
## Build and Install

--------------------
HTime is developed into a fork of the original HackRF repository, so all
build and install rules for the original HackRF software apply also to
HTime. Please, see the documentation at
<https://github.com/greatscottgadgets/hackrf>

# Documentation
## Documentation

Documentation for HackRF can be viewed on [Read the Docs](https://hackrf.readthedocs.io/en/latest/). The raw documentation files for HackRF are in the [docs folder](https://github.com/mossmann/hackrf/tree/master/docs) in this repository and can be built locally by installing [Sphinx Docs](https://www.sphinx-doc.org/en/master/usage/installation.html) and running `make html`. Documentation changes can be submitted through pull request and suggestions can be made as GitHub issues.
* [HTime — Working Principle](docs_htime/htime.md)
* [HTime USB API](docs_htime/htime_api.md)
* [HTime Tool](docs_htime/htime_tool.md)

To create a PDF of the HackRF documentation from the HackRF repository while on Ubuntu:
* run `sudo apt install latexmk texlive-latex-extra`
* navigate to hackrf/docs on command line
* run the command `make latex`
* run the command `make latexpdf`
All other documentation about the HackRF can be found
[here](https://hackrf.readthedocs.io/en/latest/).

--------------------
## License

# Getting Help
HTime © 2025 documentation by Fabrizio Pollastri is licensed under CC BY-SA 4.0

Before asking for help with HackRF, check to see if your question is listed in the [FAQ](https://hackrf.readthedocs.io/en/latest/faq.html).
HTime © 2025 software by Fabrizio Pollastri is licensed under GNU GPL v2.0

For assistance with HackRF general use or development, please look at the [issues on the GitHub project](https://github.com/greatscottgadgets/hackrf/issues). This is the preferred place to ask questions so that others may locate the answer to your question in the future.

We invite you to join our community discussions on [Discord](https://discord.gg/rsfMw3rsU8). Note that while technical support requests are welcome here, we do not have support staff on duty at all times. Be sure to also submit an issue on GitHub if you've found a bug or if you want to ensure that your request will be tracked and not overlooked.

If you wish to see past discussions and questions about HackRF, you may also view the [mailing list archives](https://pairlist9.pair.net/pipermail/hackrf-dev/).

GitHub issues on this repository that are labelled "technical support" by Great Scott Gadgets employees can expect a response time of two weeks. We currently do not have expected response times for other GitHub issues or pull requests for this repository.
142 changes: 142 additions & 0 deletions docs_htime/htime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# HTime — Working Principle

## Working Principle

The block diagram below shows the HTime extension implemented in both
the firmware and some spare hardware resources.

![HTime extension](images/time_extension.webp)

The time scale is generated starting from a spare timer (timer 3) of the
MCU that is configured to operate as a ticks counter at 200 MHz, from the
MCU clock.

Every second, the ticks counter reaches the max count of 200e6 and
generates an interrupt and restarts counting from zero. A service routine,
associated with this event, increments a software counter counting the
seconds.

The ticks counter is also used to generate a trigger signal to determine
a precise time for the start of data sampling. This is obtained loading
into one of the 4 match registers of the timer, a trigger delay in ticks
from the start of the last second. When the counter reaches this value, an
output pin goes to 1 level, while the counter continues counting up the
max count (200e6 ticks). The trigger is reset from the ticks counter
service routine at the start of the next second.

The ticks counter is also programmed to generate a pulse on an output pin
every time a max count is reached. This pulse can be used as a 1PPS output
signal for external usage.

### Clock generation

As explained above, all signals controlling the sampling timing are
generated from the MCU clock. Since these signals interact with the logic
driven by the sampling clock, it is essential that these clocks have a
constant integer frequency ratio.

For this purpose, the firmware was modified to configure the clock
generator to produce the clocks sampling, MCU, and output from the same
source, to eliminate possible phase shifts and frequency drifts among
them.

This is truly possible with HackRF board revisions different from R9 that
have the si5351c multisynthesizer with 8 clock sections, implementing a
little creative routing of the clocks on the board.
Revision board R9 has the si5351a multisynthesizer with 3 clock sections
only. In this case, two clock sections are used: section 1 for the
sampling clock and section 2 for the MCU and output clocks.

The clock generation for the tuner and the mixer is left unchanged.

## HackRF board revisions not R9

HackRF board versions with the SI5351C multisynthesizer with 8 clock
sections.

### Clock routing

A first choice was to configure the MCU to change its clock source from
the ordinary 12 MHz XTAL to the MCU input pin GP_CLKIN.

Unfortunately, not r9 boards connect to the GP_CLKIN input to the output
section 7 of the SI5351C. The PLL divisor of this section is limited to a
small range of even integer values (6-254), so it is not suitable for a
fine frequency control-like sections 0-5 offering a fractional divisor
with 20 bits resolution.

Fortunately, the MCU allows the input external clock (GP_CLKIN) to be
mapped alternatively on pin 120 (PF_4), that is connected to connector
P22 pin 12 (signal i2s0_rx_sck). So, it is possible to connect a jumper
from this to connector p22 pin1 (signal clkout) and use the clkout clock
as input clock for the MCU.

![HackRF Clock Generation not R9 boards](images/hackrf_clock_gen_not_r9.webp)

![Output clock jumper for not R9 board revisions](images/hackrf_clkout_jumper.png)

The SI5351C allows to configure the block of sections 0-3 to be all
driven by the PLL frequency of section 0. So, the phase and frequencies
generated by sections 0-3 are coherent and cannot drift. Since sections
0, 1, and 2 generate the clocks needed for data sampling, in this way
also the MCU clock becomes coherent with the sampling clock, and the
requirement of the previous section is met.

Since clkout comes from section 3 of the si5351c, and this section can be
configured to take as input clock the output of section 0 a full
resolution divisor (20 bits), suitable for the clock frequency adjustments
required by GSCV frequency control.

### Trigger routing

The trigger is generated by the match register 0 of the ticks counter.
This event is configured to change the level of the MCU output P2_3,
pin 87 (signal i2c1_sda) that is connected to connector p22, pin 6.
The HackRF board already has a trigger input on connector p28, pin 16.
When this input goes to 1 level, it enables the data sampling.
So, the generated trigger can be easily routed with a wire jumper
to the sampling trigger input, to do the job.

![Sampling trigger jumper for all board revisions](images/hackrf_trigger_jumper.png)

## HackRF board revision R9

HackRF board version with SI5351A multisynthesizer with 3 clock sections.

> **Note — Best synchronization results are obtained with HackRF One
> board revision R9.**
> On R9, the sampling clock and the MCU clock are both generated by a
> single section of the SI5351A PLL synthesizer, so all clocks share the
> same phase reference and no inter-clock phase jumps can occur. On all
> other board revisions, two separate sections of PLL synthesizers are
> involved (SI5351C): one drives the sampling clock and another drives
> the MCU clock. Because these two PLLs are independent, a random phase
> jump between the two clocks may occasionally occur — approximately
> 100 ns at a 10 MHz sampling rate — causing a transient timing error in
> the synchronization.

### Clock routing

Also for revision R9, the choice was to configure the MCU to change its
clock source from the ordinary 12 MHz XTAL to the MCU input pin GP_CLKIN.

In this board version, GP_CLKIN is already connected to the clkout from
section 2 of the multisynthesizer. No jumpers are needed. While the
multisynthesizer section 1 generates the sampling clocks. These
frequencies are adjusted by software updating the parameters in
multisynthesizer sections 1 and 2. Since these updates cannot be
simultaneous, a small phase shift is added at each change. To minimize
this effect, the change order is reversed after each change.

The clock generation for the tuner and the mixer from the
multisynthesizer section 0 is left unchanged.

![HackRF Clock Generation R9 boards](images/hackrf_clock_gen_r9.webp)

### Trigger routing

The trigger routing is the same as the other HackRF board revisions.

---

<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><span property="dct:title">HTime documentation</span> by <span property="cc:attributionName">Fabrizio Pollastri</span> is licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-SA 4.0</a></p>
107 changes: 107 additions & 0 deletions docs_htime/htime_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# HTime USB API

This API allows access to all the firmware features of HTime. An example
of application can be seen in the
[HTime Tool source](../host/hackrf-tools/src/hackrf_time.c) and its
[usage documentation](htime_tool.md).

---

**`hackrf_time_set_divisor_next_pps(device, divisor)`**

Set **divisor** value (uint32_t) into the ticks counter at the next PPS.
With the HackRF MCU working @ 200 MHz, the ticks counter must be set to
200000000-1 to obtain a counting period of 1 second. **device** is a
pointer to the HackRF device.

---

**`hackrf_time_set_divisor_one_pps(device, divisor)`**

Set **divisor** value (uint32_t) into the ticks counter from the next PPS
for only one counter cycle then restore the previous divisor value.
**device** is a pointer to the HackRF device.

---

**`hackrf_time_set_trig_delay_next_pps(device, trig_delay)`**

Set **trig_delay** value (uint32_t) as the sampling trigger delay at next
PPS. The trigger delay is from the start of the second (PPS leading edge)
in tick units. **device** is a pointer to the HackRF device.

---

**`hackrf_time_get_seconds_now(device, &seconds)`**

Get the value of the second counter immediately into **seconds**
(* int64_t). **device** is a pointer to the HackRF device.

---

**`hackrf_time_set_seconds_now(device, seconds)`**

Set the **seconds** value (int64_t) immediately into the second counter.
**device** is a pointer to the HackRF device.

---

**`hackrf_time_set_seconds_next_pps(device, seconds)`**

Set the **seconds** value (int64_t) at next PPS into the second counter.
**device** is a pointer to the HackRF device.

---

**`hackrf_time_get_ticks_now(device, &ticks)`**

Get the value of the tick counter immediately into **ticks**
(* uint32_t). **device** is a pointer to the HackRF device.

---

**`hackrf_time_set_ticks_now(device, ticks)`**

Set the **ticks** value (uint32_t) immediately into the tick counter.
**device** is a pointer to the HackRF device.

---

**`hackrf_time_set_clk_freq(device, clk_freq)`**

Set the **clk_freq** value (double) as the sampling rate synchronized to
the MCU clock. Must be a value near (+-100 Hz) 10 MHz. **device** is a
pointer to the HackRF device.

---

**`hackrf_time_set_mcu_clk_sync(device, enable)`**

Set the **enable** value (uint8_t) of the MCU synchronized clock mode.
When this mode is on, all relevant clocks are kept in sync to avoid phase
shifts and frequency drifts. **device** is a pointer to the HackRF
device.

---

**`hackrf_time_set_trig_hold_enable_next_pps(device, enable)`**

Set the **enable** value (uint8_t) of trigger hold at the next PPS
leading edge. When enabled, the trigger trailing edge action is disabled,
so the trigger output can stay active across the second boundary
(long capture crossing second border). When disabled, the trigger
trailing edge is cleared as usual, just after next PPS leading edge.
**device** is a pointer to the HackRF device.

---

**`hackrf_time_set_pps_out_enable_next_pps(device, enable)`**

Set the **enable** value (uint8_t) of PPS output at the next PPS leading
edge. When enabled, regular PPS output pulses are generated.
When disabled, PPS output is kept low (inactive). **device** is a pointer
to the HackRF device.

---

<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><span property="dct:title">HTime documentation</span> by <span property="cc:attributionName">Fabrizio Pollastri</span> is licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-SA 4.0</a></p>
46 changes: 46 additions & 0 deletions docs_htime/htime_tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# HTime Tool

The HTime Tool is a host application for accessing the API of the HTime
extension.

## Tool help

```
Manage HTime for HackRF: ticks, seconds, A/D trigger, clocks.

Usage:
-h, --help: this help
-d, --divisor <v>: set divisor to <v> val next pps
-D, --Divisor <v>: set divisor to <v> val for one pps
-f, --set_clk_freq <v>: set sync clock freq to <v>
-k, --ticks : read ticks counter now
-K, --Ticks : set ticks counter now
-l, --trig_hold <0/1>: enable/disable trig hold status on next pps
-p, --pps_out <0/1>: enable/disable pps output on next pps
-r, --trig_delay <v>: set trig delay to <v> val next pps
-s, --seconds <v>: set seconds counter now to <v> value
-S, --Seconds <v>: as -s but in sync to next pps
-t, --get_seconds: read seconds counter now
-y, --mcu_clk_sync <0/1>: enable/disable mcu clock sync

Examples:
hackrf_time -s 1234 # set seconds counter to 1234 now
hackrf_time -l 1 # enable trigger hold on next PPS
hackrf_time -p 0 # disable PPS output on next PPS
hackrf_time -y 1 # enable MCU sync mode

v0.1.0 20250215 F.P. <mxgbot@gmail.com>
```

Each tool option corresponds to one API function of HTime. For a more
detailed explanation of each function, see
[HTime USB API](htime_api.md).

## Source

The tool source code is at
[host/hackrf-tools/src/hackrf_time.c](../host/hackrf-tools/src/hackrf_time.c).

---

<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><span property="dct:title">HTime documentation</span> by <span property="cc:attributionName">Fabrizio Pollastri</span> is licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-SA 4.0</a></p>
Binary file added docs_htime/images/hackrf_clkout_jumper.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs_htime/images/hackrf_clock_gen_not_r9.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs_htime/images/hackrf_clock_gen_r9.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs_htime/images/hackrf_trigger_jumper.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs_htime/images/time_extension.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions firmware/common/clkin.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ void clkin_detect_init(void)

reset.first_tcr = TIMER_TCR_CEN | TIMER_TCR_CRST;
reset.second_tcr = TIMER_TCR_CEN;
timer_dma_lli.src = (uint32_t) & (reset);
timer_dma_lli.dest = (uint32_t) & (TIMER2_TCR);
timer_dma_lli.next_lli = (uint32_t) & (timer_dma_lli);
timer_dma_lli.src = (uint32_t) &(reset);
timer_dma_lli.dest = (uint32_t) &(TIMER2_TCR);
timer_dma_lli.next_lli = (uint32_t) &(timer_dma_lli);
timer_dma_lli.control = GPDMA_CCONTROL_TRANSFERSIZE(2) |
GPDMA_CCONTROL_SBSIZE(0) // 1
| GPDMA_CCONTROL_DBSIZE(0) // 1
Expand Down
Loading