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