// --------------------------------------------------------------------------- // baro_i2c.sv — QX-250 FPGA block B4 (barometer I2C sequencer) // FPGA-QX250-001 · traces REQ-CTRL-002, functional F6 // // Drives a DPS310 over the i2c_master byte engine: // 1. INIT — write PRS_CFG/TMP_CFG/MEAS_CFG/CFG (continuous pressure+temp). // 2. RUN — on each baro_tick (B5, ~50 Hz), read 6 bytes from PSR_B2 (0x00): // pressure[23:0] then temperature[23:0]; pulse data_valid. // // Register/ACK sequence validated in /tmp/b4_model.py. // --------------------------------------------------------------------------- `timescale 1ns/1ps `default_nettype none module baro_i2c #( parameter int DIV4 = 30, // i2c quarter-bit (48MHz→400kHz) parameter [6:0] DEV = 7'h77 // DPS310 I2C address ) ( input wire clk, input wire rst_n, input wire baro_tick, // from B5 // I2C pins (open-drain enables + inputs; board adds tri-state pads) output wire scl_oe, output wire sda_oe, input wire sda_in, input wire scl_in, output reg [23:0] pressure, output reg [23:0] temperature, output reg data_valid, output reg initialized ); localparam [2:0] OP_START=3'd0, OP_RSTART=3'd1, OP_WBYTE=3'd2, OP_RBYTE=3'd3, OP_STOP=3'd4; wire [7:0] ADDR_W = {DEV, 1'b0}; wire [7:0] ADDR_R = {DEV, 1'b1}; // ---- byte engine ------------------------------------------------------- reg [2:0] op; reg [7:0] wr_data; reg ack_out, go; wire done, ack_in; wire [7:0] rd_data; i2c_master #(.DIV4(DIV4)) u_i2c ( .clk(clk), .rst_n(rst_n), .op(op), .go(go), .wr_data(wr_data), .ack_out(ack_out), .done(done), .ack_in(ack_in), .rd_data(rd_data), .scl_oe(scl_oe), .sda_oe(sda_oe), .sda_in(sda_in), .scl_in(scl_in) ); // ---- DPS310 config table {reg, data} ----------------------------------- localparam int N_CFG = 4; function automatic [15:0] cfg_entry(input int i); case (i) 0: cfg_entry = {8'h06, 8'h26}; // PRS_CFG: 32/s, x16 oversample 1: cfg_entry = {8'h07, 8'hA0}; // TMP_CFG: ext sensor, 8/s 2: cfg_entry = {8'h08, 8'h07}; // MEAS_CFG: continuous P+T 3: cfg_entry = {8'h09, 8'h00}; // CFG_REG: no FIFO/int default: cfg_entry = 16'h0000; endcase endfunction // ---- sequencer --------------------------------------------------------- typedef enum logic [4:0] { S_POR, I_START, I_ADDRW, I_REG, I_DATA, I_STOP, I_NEXT, S_IDLE, R_START, R_ADDRW, R_REG, R_RSTART, R_ADDRR, R_READ, R_STOP, R_DONE } st_t; st_t state; reg [2:0] cfg_idx; reg [2:0] rd_idx; // 0..5 reg issued; reg [7:0] buf [0:5]; wire [15:0] cur_cfg = cfg_entry(cfg_idx); integer k; // issue one micro-op: (o, d, a) then wait for done -> `adv` becomes usable // via the `issued`/`done` handshake inlined per state. task automatic launch(input [2:0] o, input [7:0] d, input a); op <= o; wr_data <= d; ack_out <= a; go <= 1'b1; issued <= 1'b1; endtask always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin state<=S_POR; cfg_idx<=0; rd_idx<=0; issued<=1'b0; go<=1'b0; op<=OP_START; wr_data<=0; ack_out<=1'b0; pressure<=0; temperature<=0; data_valid<=1'b0; initialized<=1'b0; for (k=0;k<6;k=k+1) buf[k]<=8'd0; end else begin go <= 1'b0; // go is a 1-cycle strobe data_valid <= 1'b0; case (state) S_POR: begin cfg_idx<=0; state<=I_START; issued<=1'b0; end // -------- INIT: per config reg -> START,W(addrW),W(reg),W(data),STOP I_START: if (!issued) launch(OP_START,8'h00,1'b0); else if (done) begin issued<=0; state<=I_ADDRW; end I_ADDRW: if (!issued) launch(OP_WBYTE,ADDR_W,1'b0); else if (done) begin issued<=0; state<=I_REG; end I_REG: if (!issued) launch(OP_WBYTE,cur_cfg[15:8],1'b0); else if (done) begin issued<=0; state<=I_DATA; end I_DATA: if (!issued) launch(OP_WBYTE,cur_cfg[7:0],1'b0); else if (done) begin issued<=0; state<=I_STOP; end I_STOP: if (!issued) launch(OP_STOP,8'h00,1'b0); else if (done) begin issued<=0; state<=I_NEXT; end I_NEXT: begin if (cfg_idx == N_CFG-1) begin initialized<=1'b1; state<=S_IDLE; end else begin cfg_idx<=cfg_idx+3'd1; state<=I_START; end end // -------- RUN -------- S_IDLE: if (baro_tick) begin rd_idx<=0; issued<=1'b0; state<=R_START; end R_START: if (!issued) launch(OP_START,8'h00,1'b0); else if (done) begin issued<=0; state<=R_ADDRW; end R_ADDRW: if (!issued) launch(OP_WBYTE,ADDR_W,1'b0); else if (done) begin issued<=0; state<=R_REG; end R_REG: if (!issued) launch(OP_WBYTE,8'h00,1'b0); // PSR_B2 base else if (done) begin issued<=0; state<=R_RSTART; end R_RSTART: if (!issued) launch(OP_RSTART,8'h00,1'b0); else if (done) begin issued<=0; state<=R_ADDRR; end R_ADDRR: if (!issued) launch(OP_WBYTE,ADDR_R,1'b0); else if (done) begin issued<=0; state<=R_READ; end R_READ: if (!issued) launch(OP_RBYTE,8'h00,(rd_idx==3'd5)); // NACK last else if (done) begin issued<=0; buf[rd_idx]<=rd_data; if (rd_idx==3'd5) state<=R_STOP; else rd_idx<=rd_idx+3'd1; end R_STOP: if (!issued) launch(OP_STOP,8'h00,1'b0); else if (done) begin issued<=0; state<=R_DONE; end R_DONE: begin pressure <= {buf[0], buf[1], buf[2]}; temperature <= {buf[3], buf[4], buf[5]}; data_valid <= 1'b1; state <= S_IDLE; end default: state<=S_POR; endcase end end endmodule `default_nettype wire