1//
2// Copyright 2015 Ettus Research, A National Instruments Company
3// SPDX-License-Identifier: LGPL-3.0
4//
5// Description: AXI PMU
6//
7
8module axi_pmu
9#(
10  parameter DEPTH = 64
11)
12(
13  // sys connect
14  input         s_axi_aclk,
15  input         s_axi_areset,
16
17  // spi slave port
18  input         ss,
19  input         mosi,
20  input         sck,
21  output        miso,
22
23  // axi4 lite slave port
24  input [31:0]  s_axi_awaddr,
25  input         s_axi_awvalid,
26  output        s_axi_awready,
27
28  input [31:0]  s_axi_wdata,
29  input [3:0]   s_axi_wstrb,
30  input         s_axi_wvalid,
31  output        s_axi_wready,
32
33  output [1:0]  s_axi_bresp,
34  output        s_axi_bvalid,
35  input         s_axi_bready,
36
37  input [31:0]  s_axi_araddr,
38  input         s_axi_arvalid,
39  output        s_axi_arready,
40
41  output [31:0] s_axi_rdata,
42  output [1:0]  s_axi_rresp,
43  output        s_axi_rvalid,
44  input         s_axi_rready,
45
46  output        s_axi_irq
47);
48
49  wire             spi_stb;
50  wire [DEPTH-1:0] spi_rx;
51  wire [DEPTH-1:0] spi_tx;
52
53  spi_slave inst_spi_slave0
54  (
55    .clk(s_axi_aclk),
56    .rst(s_axi_areset),
57
58    .ss(ss),
59    .mosi(mosi),
60    .miso(miso),
61    .sck(sck),
62
63    .parallel_stb(spi_stb),
64    .parallel_din(spi_tx),
65    .parallel_dout(spi_rx)
66  );
67
68  wire [7:0] rx_type = spi_rx[7:0];
69
70  reg [DEPTH-1:0] spi_rx_r0, spi_rx_r1, spi_rx_r2;
71  always @ (posedge s_axi_aclk)
72    if (s_axi_areset) begin
73      spi_rx_r0 <= 64'h0000_0000_0000_0000;
74      spi_rx_r1 <= 64'h0000_0000_0000_0000;
75      spi_rx_r2 <= 64'h0000_0000_0000_0000;
76    end else begin
77      spi_rx_r0 <= spi_stb && (rx_type == 0) ? spi_rx : spi_rx_r0;
78      spi_rx_r1 <= spi_stb && (rx_type == 1) ? spi_rx : spi_rx_r1;
79      spi_rx_r2 <= spi_stb && (rx_type == 2) ? spi_rx : spi_rx_r2;
80    end
81
82  localparam IDLE              = 3'b001;
83  localparam READ_IN_PROGRESS  = 3'b010;
84  localparam WRITE_IN_PROGRESS = 3'b100;
85
86  reg [2:0] state;
87  reg [7:0] addr;
88
89  always @ (posedge s_axi_aclk) begin
90    if (s_axi_areset) begin
91      state <= IDLE;
92    end
93    else case (state)
94
95    IDLE: begin
96      if (s_axi_arvalid) begin
97        state <= READ_IN_PROGRESS;
98        addr  <= s_axi_araddr[7:0];
99      end
100      else if (s_axi_awvalid) begin
101        state <= WRITE_IN_PROGRESS;
102        addr  <= s_axi_awaddr[7:0];
103      end
104    end
105
106    READ_IN_PROGRESS: begin
107      if (s_axi_rready)
108        state <= IDLE;
109    end
110
111    WRITE_IN_PROGRESS: begin
112      if (s_axi_bready)
113        state <= IDLE;
114    end
115
116    default: begin
117      state <= IDLE;
118    end
119
120    endcase
121  end
122
123  // write mux
124  reg write_shutdown;
125  reg write_irq_mask;
126
127  always @(*) begin
128    write_shutdown = 1'b0;
129    write_irq_mask = 1'b0;
130
131    if (state == WRITE_IN_PROGRESS)
132      case (addr)
133        8'h00: write_shutdown = 1'b1;
134        8'h04: write_irq_mask = 1'b1;
135      endcase
136  end
137
138  reg [31:0] shutdown = 32'h0000_0000;
139  always @ (posedge s_axi_aclk) begin
140    if (s_axi_areset)
141      shutdown <= 32'h0000_0000;
142    else if (write_shutdown)
143      shutdown <= s_axi_wdata;
144  end
145
146  wire [31:0] spi_tx_tdata;
147  wire        spi_tx_tvalid;
148  wire [5:0] spi_tx_occupied;
149  wire [5:0] spi_tx_space;
150
151  wire [31:0] tmux = write_shutdown ? {s_axi_wdata[23:0], 8'h00}
152                                    : {s_axi_wdata[7:0], s_axi_wdata[15:8], addr[7:0], 8'h01};
153
154  wire is_spi_cmd = (addr[7:0] == 8'h00) || (addr[7:0] > 8'h04);
155
156  axi_fifo_bram #(.WIDTH(32), .SIZE(5)) axi_fifo_short_inst
157  (
158    .clk(s_axi_aclk),
159    .reset(s_axi_areset),
160    .clear(1'b0),
161    .i_tdata(tmux),
162    .i_tvalid(state == WRITE_IN_PROGRESS && is_spi_cmd),
163    .i_tready(),
164    .o_tdata(spi_tx_tdata),
165    .o_tvalid(spi_tx_tvalid),
166    .o_tready(spi_stb),
167    .occupied(spi_tx_occupied),
168    .space(spi_tx_space)
169  );
170
171  reg [63:0] spi_tx_reg;
172
173  always @ (posedge s_axi_aclk)
174    if(s_axi_areset)
175      spi_tx_reg <= 64'h0000_0000_0000_0000;
176    else if (spi_stb)
177      spi_tx_reg <= {spi_tx_tvalid, 31'h00, spi_tx_tdata};
178
179  assign spi_tx = spi_tx_reg;
180
181  /* battery stuff */
182  wire [15:0] battery_voltage      = {spi_rx_r0[55:48], spi_rx_r0[63:56]};
183  wire [1:0]  battery_temp_alert   =  spi_rx_r0[47:46];
184  wire        battery_online       =  spi_rx_r0[45];
185  wire [2:0]  battery_health       =  spi_rx_r0[44:42];
186  wire [1:0]  battery_status       =  spi_rx_r0[41:40];
187
188  /* charger stuff */
189  /* unused [39:38] */
190  wire [1:0]  charger_health       = spi_rx_r0[37:36];
191  wire        charger_online       = spi_rx_r0[35];
192  /* unused bit 34 */
193  wire [1:0]  charger_charge_type  = spi_rx_r0[33:32];
194
195  /* gauge stuff */
196  wire [7:0]  gauge_status         =  spi_rx_r1[63:56];
197  wire [15:0] voltage              = {spi_rx_r1[47:40], spi_rx_r1[55:48]};
198  wire [15:0] temp                 = {spi_rx_r1[31:24], spi_rx_r1[39:32]};
199  wire [15:0] charge_acc           = {spi_rx_r1[15:8] , spi_rx_r1[23:16]};
200
201  /* charge last full */
202  wire [15:0] charge_last_full     = {spi_rx_r2[15:8], spi_rx_r2[23:16]};
203
204  /* settings flags */
205  wire [7:0] settings = spi_rx_r2[31:24];
206
207  reg [7:0] irq_enable;
208  always @ (posedge s_axi_aclk) begin
209    if (s_axi_areset)
210      irq_enable <= 8'h00;
211    else if (write_irq_mask)
212      irq_enable <= s_axi_wdata[15:8];
213  end
214
215  wire [7:0] irq_status = gauge_status;
216  assign s_axi_irq = |(irq_status & irq_enable);
217
218  wire [3:0] version_maj = spi_rx_r0[15:12];
219  wire [3:0] version_min = spi_rx_r0[11:8];
220
221  reg [31:0] rdata;
222  // read mux
223  always @(*) begin
224    rdata = 32'hdead_beef;
225
226    if (state == READ_IN_PROGRESS)
227      case (addr)
228        8'h00: rdata = shutdown;
229        8'h04: rdata = {16'h0000, irq_enable, version_maj, version_min};
230        8'h08: rdata = {8'h0, battery_voltage, battery_temp_alert, battery_online, battery_health, battery_status};
231        8'h0c: rdata = {27'd0, charger_charge_type, charger_online, charger_health};
232        8'h10: rdata = {temp, charge_acc};
233        8'h14: rdata = {8'h00, gauge_status, voltage};
234        8'h18: rdata = {16'h0000, charge_last_full};
235        8'h1c: rdata = {24'd0, settings};
236      endcase
237  end
238
239  assign s_axi_arready = (state == IDLE);
240  assign s_axi_rvalid  = (state == READ_IN_PROGRESS);
241  assign s_axi_rresp   = 2'b00;
242
243  assign s_axi_rdata   = rdata;
244
245  assign s_axi_awready = (state == IDLE);
246  assign s_axi_wready  = (state == WRITE_IN_PROGRESS);
247  assign s_axi_bresp   = 2'b00;
248  assign s_axi_bvalid  = (state == WRITE_IN_PROGRESS);
249
250endmodule
251