aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/ad5662_auto_spi.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/control/ad5662_auto_spi.v')
-rw-r--r--fpga/usrp3/lib/control/ad5662_auto_spi.v99
1 files changed, 99 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/control/ad5662_auto_spi.v b/fpga/usrp3/lib/control/ad5662_auto_spi.v
new file mode 100644
index 000000000..d9f2e53be
--- /dev/null
+++ b/fpga/usrp3/lib/control/ad5662_auto_spi.v
@@ -0,0 +1,99 @@
+//
+// Copyright 2015 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// The AD5662 DAC serial interface uses 24-bit transfers to encode 16-bits
+// of actual data, two bits for power-down mode, and six pad bits. This
+// module stores a copy of the last-programmed value, and will generate a
+// serial stream if ever the input word (dat) changes. It will ignore
+// changes to (dat) while it is busy with a serial update.
+//
+module ad5662_auto_spi
+(
+ input clk,
+ input [15:0] dat,
+ output reg sclk,
+ output reg mosi,
+ output reg sync_n
+);
+ // initialize ldat to 0, thus forcing
+ // a reload on init.
+ reg [15:0] ldat = 16'd0;
+ wire upd = (dat != ldat); // new data present, need to update hw
+
+ reg [23:0] shft=24'b0;
+ wire [23:0] nxt_shft;
+
+ // clock cycle counter to throttle generated spi cycles
+ // allowing one spi clock cycle every 16 cycles of clk, with clk at 200 MHz
+ // gives a spi clock rate of 12 MHz. This can be made more sophisticated
+ // or parameterized, if more flexibility in clk is needed, of course.
+ reg [3:0] ccnt=4'b0;
+ wire [3:0] nxt_ccnt = ccnt + 1'b1;
+ wire half = ccnt==4'b1000;
+ wire full = ccnt==4'b1111;
+ reg sena, hena;
+ wire cena;
+ always @(posedge clk) if (cena) ccnt <= nxt_ccnt;
+ always @(posedge clk) sena <= full; // state updates and rising sclk
+ always @(posedge clk) hena <= half; // for falling sclk
+
+ // transfer state counter
+ reg [4:0] scnt = 5'b0;
+ reg [4:0] nxt_scnt;
+ always @(posedge clk) begin
+ if (sena) begin
+ scnt <= nxt_scnt;
+ shft <= nxt_shft;
+ mosi <= shft[23];
+ end
+ end
+
+
+ // 32 possible states - more than enough to shift-out 24 bits and manage
+ // the sync_n line
+
+ // particular scnt values of interest
+ localparam READY=5'b00000; // waiting for new data
+ localparam DCAPT=5'b00001; // new data transfers into ldat
+ localparam SYNCL=5'b00010; // assert sync_n low
+ localparam SYNCH=5'b11011; // return sync_n high
+
+ assign cena = upd | scnt != READY;
+
+ always @(scnt or upd)
+ begin
+ case (scnt)
+ READY:
+ nxt_scnt = upd ? DCAPT : READY;
+ SYNCH:
+ nxt_scnt = READY;
+ default:
+ nxt_scnt = scnt + 1'b1;
+ endcase
+ end
+
+ // note: defining the power-down mode bits to 00 for "normal operation"
+ assign nxt_shft = (scnt == SYNCL) ? { 8'b000000_00, ldat } : { shft[22:0], 1'b0 };
+
+ // Update ldat when dat has changed, but only if READY.
+ // Changes to dat arriving faster than can be kept up with here are ignored
+ // until the cycle-in-progress is completed.
+ wire ldat_ena = sena & (scnt == DCAPT);
+ always @(posedge clk) begin
+ if (ldat_ena) ldat <= dat;
+ end
+
+ // keep the sync_n line low when idle to minimize power consumption
+ // it gets brought high just before beginning each transaction
+ wire nxt_sync_n = (scnt==SYNCL) | (scnt==SYNCH);
+ always @(posedge clk) if (sena) sync_n <= nxt_sync_n;
+
+ reg sclk_go;
+ always @(posedge clk) sclk_go <= (scnt > SYNCL);
+ wire nxt_sclk = ~sclk_go ? 1'b1 : ~sclk;
+ always @(posedge clk) if (sena | hena) sclk <= nxt_sclk;
+
+endmodule