diff options
Diffstat (limited to 'fpga/usrp3/lib/control/ad5662_auto_spi.v')
-rw-r--r-- | fpga/usrp3/lib/control/ad5662_auto_spi.v | 99 |
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 |