aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/control/ad5662_auto_spi.v
blob: d9f2e53beb51f1d9bff437d1a7c57929443e6e28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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