summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Eid <mark.eid@ni.com>2023-07-18 16:42:46 -0500
committerWade Fife <wade.fife@ettus.com>2023-07-31 17:35:44 -0500
commit5da42f3542ed555623c73c7ce487b9960849f209 (patch)
tree7c9479c6e334e0e5fba7e82f20f06816d91fe891
parentdocs: update FPGA build docs (diff)
downloaduhd-5da42f3542ed555623c73c7ce487b9960849f209.tar.xz
uhd-5da42f3542ed555623c73c7ce487b9960849f209.zip
fpga: x400: Fix SPI trigger clock crossing
-rw-r--r--fpga/usrp3/top/x400/x4xx_gpio_spi.v45
1 files changed, 37 insertions, 8 deletions
diff --git a/fpga/usrp3/top/x400/x4xx_gpio_spi.v b/fpga/usrp3/top/x400/x4xx_gpio_spi.v
index 25591d570..e9d3c0fc0 100644
--- a/fpga/usrp3/top/x400/x4xx_gpio_spi.v
+++ b/fpga/usrp3/top/x400/x4xx_gpio_spi.v
@@ -369,19 +369,48 @@ module x4xx_gpio_spi #(
assign readback_stb_extended = readback_stb;
`else // X410
- // Register set_stb for use in 2x domain.
+
+ // We only trigger one cycle of set_stb_2x per state change. This way the latency
+ // is deterministic from the first change and aligned to the correct address,
+ // without the need to pipeline the signal.
+ // ┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
+ //clk : └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └───
+ // xxxxxx/ \/ \/ \xxxxxxxxxxxxxxxxxxxxxx
+ //state : xxxxxx\ ctrl /\ div /\ data /xxxxxxxxxxxxxxxxxxxxxx
+ // xxxxxxxxxxxxxxxxxx/ \/ \/ \xxxxxxxxxx
+ //state_dlyd : xxxxxxxxxxxxxxxxxx\ ctrl /\ div /\ data /xxxxxxxxxx
+ // ┐ ┌───────────────────────────────────┐
+ //set_stb : └─────────────────┘ └─────────
+ // xxxxxxxxxxxxxxxxxx/ \/ \/ \xxxxxxxxxx
+ //set_addr : xxxxxxxxxxxxxxxxxx\ 0x1 /\ 0x0 /\ 0x2 /xxxxxxxxxx
+ // ──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐
+ //clk_2x : └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └
+ // xxxxxxxxxxxxxxxxxxxxxxxx/ \/ \/ \xxxx
+ //state_dlyd_2x: xxxxxxxxxxxxxxxxxxxxxxxx\ ctrl /\ div /\ data /xxxx
+ // ┌─────┐ ┌─────┐ ┌─────┐
+ //set_stb_2x : ────────────────────────┘ └─────┘ └─────┘ └─────────
+ // ┌─────┐
+ //trigger_spi : ──────────────────────────────────────────────────────┘ └───
+ //
+
reg set_stb_2x = 1'b0;
- reg ctrlport_clk_phase = 1'b1;
+ reg [2:0] state_dlyd, state_dlyd_2x = IDLE;
+
+ always @ (posedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ state_dlyd <= IDLE;
+ end else begin
+ state_dlyd <= state;
+ end
+ end
always @ (posedge ctrlport_clk_2x) begin
if (ctrlport_rst) begin
- ctrlport_clk_phase <= 1'b1;
- set_stb_2x <= 1'b0;
+ state_dlyd_2x <= IDLE;
+ set_stb_2x <= 1'b0;
end else begin
- // Assert strobe only during a single 2x cycle of the
- // 1x pulse, when 1x clock is low.
- set_stb_2x <= ctrlport_clk_phase & set_stb;
- ctrlport_clk_phase <= ~ctrlport_clk_phase;
+ state_dlyd_2x <= state_dlyd;
+ set_stb_2x <= set_stb && (state_dlyd_2x != state_dlyd);
end
end