module BUS_Con ( input i_sys_clk, input i_rst_n, // SPI Interface input i_sclk, input i_mosi, input i_cs, output o_miso, // <-- MISO Logic added here // Local Interface output o_rvalid, // (Typo in original: o_rvalid) input i_rvalid, // Valid signal from Backend (Data is ready) input i_wvalid, output o_wready, input i_reg_busy, input [23:0] i_rdata, // Data from Backend to send to Master output o_wr, output o_cmd_valid, output [23:0] o_data, output [6:0] o_addr, output o_err ); // --- 1. Synchronization --- reg [1:0] cs_sync; reg [1:0] sclk_sync; reg [1:0] mosi_sync; wire cs_active; wire cs_rise; wire sclk_rise; // For Sampling MOSI wire sclk_fall; // For Shifting MISO wire mosi_data; always @(posedge i_sys_clk) begin if (!i_rst_n) begin cs_sync <= 2'b11; sclk_sync <= 2'b00; mosi_sync <= 2'b00; end else begin cs_sync <= {cs_sync[0], i_cs}; sclk_sync <= {sclk_sync[0], i_sclk}; mosi_sync <= {mosi_sync[0], i_mosi}; end end assign cs_active = ~cs_sync[1]; assign cs_rise = (!cs_sync[1] && cs_sync[0]); // SPI Mode 0: Sample MOSI on Rising, Shift MISO on Falling assign sclk_rise = (!sclk_sync[1] && sclk_sync[0]); assign sclk_fall = (sclk_sync[1] && !sclk_sync[0]); // Logic for MISO assign mosi_data = mosi_sync[1]; // --- 2. MOSI (Receive) Logic --- reg [31:0] recv_reg; reg [5:0] bit_cnt; reg data_ready; reg err_flag; reg [1:0] state; // reg [31:0] tx_buffer; // Holds data waiting for the next CS Low reg [31:0] miso_shift; // The actual shifter // Output Registers reg [6:0] addr_out; reg [23:0] data_out; reg wr_out; reg cmd_valid_out; // localparam IDLE = 2'b00; localparam CMD_SENT = 2'b01; localparam WAIT_BUSY = 2'b10; localparam DONE = 2'b11; always @(posedge i_sys_clk) begin if (!i_rst_n) begin recv_reg <= 32'd0; bit_cnt <= 6'd0; data_ready <= 1'b0; err_flag <= 1'b0; end else begin // Clear data_ready when FSM has consumed the command if (state == CMD_SENT) begin data_ready <= 1'b0; end if (cs_active) begin if (sclk_rise) begin recv_reg <= {recv_reg[30:0], mosi_data}; if (bit_cnt == 6'd7) begin // 8th bit received: check if read (MSB=0) or write (MSB=1) if (recv_reg[0] == 1'b0) begin // Read command: set data_ready after 8 bits data_ready <= 1'b1; end end else if (bit_cnt == 6'd31) begin // 32nd bit received: write command complete data_ready <= 1'b1; end bit_cnt <= bit_cnt + 1'b1; end else if (cs_rise) begin if (bit_cnt == 6'd32) begin data_ready <= 1'b1; err_flag <= 1'b0; bit_cnt <= 6'd0; end else if (bit_cnt != 0) begin err_flag <= 1'b1; bit_cnt <= 6'd0; recv_reg <= 'd0; end end end if (state == DONE) begin recv_reg <= 'd0; end end end // --- 3. MISO (Transmit) Logic --- // Protocol: We shift out 32 bits. // Format: [8 bit Status/Padding] + [24 bit i_rdata] // Capture data from backend when valid always @(posedge i_sys_clk) begin if (!i_rst_n) begin tx_buffer <= 32'd0; end else begin // If backend provides valid read data, store it. // We pad the top 8 bits with Zeros (or you can put status flags here) if (i_rvalid) begin tx_buffer <= {8'h00, i_rdata}; end end end // Shift data out always @(posedge i_sys_clk) begin if (!i_rst_n) begin miso_shift <= 32'd0; end else begin if (!cs_active) begin // Reset shifter while CS is High miso_shift <= 32'd0; end else if (sclk_fall) begin // Shift on Falling Edge (Master samples on Rising) miso_shift <= {miso_shift[30:0], 1'b0}; end // Load register data when read completes (takes priority over shift) if (i_rvalid) begin miso_shift <= {8'h00, i_rdata}; end end end // Tri-state MISO when CS is high (optional, usually good practice) // If your board doesn't need tristate, just use: assign o_miso = miso_shift[31]; assign o_miso = (cs_active) ? miso_shift[31] : 1'bz; // --- 4. Register Control FSM --- always @(posedge i_sys_clk) begin if (!i_rst_n) begin state <= IDLE; cmd_valid_out <= 1'b0; addr_out <= 7'd0; data_out <= 24'd0; wr_out <= 1'b0; end else begin case (state) IDLE: begin if (data_ready) begin addr_out <= recv_reg[30:24]; data_out <= recv_reg[23:0]; wr_out <= recv_reg[31]; if (!i_reg_busy) begin cmd_valid_out <= 1'b1; state <= CMD_SENT; end else begin state <= WAIT_BUSY; end end end WAIT_BUSY: begin if (!i_reg_busy) begin cmd_valid_out <= 1'b1; state <= CMD_SENT; end end CMD_SENT: begin cmd_valid_out <= 1'b0; state <= DONE; end DONE: begin if (!i_reg_busy) begin state <= IDLE; addr_out <= 'd0; data_out <= 'd0; wr_out <= 'd0; end end endcase end end assign o_addr = addr_out; assign o_data = data_out; assign o_wr = wr_out; assign o_cmd_valid = cmd_valid_out; assign o_err = err_flag; assign o_wready = (state == IDLE); // Pass through unused signal or hook it up if needed assign o_rvalid = i_rvalid; endmodule