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