I2C Slave Addressing Modification
3:56
Verilog
module i2c_slave( input clk, input rst, inout sda, input scl, input sda_temp, // external (open-drain pullup) signal on sda output reg wr_en, output reg rd_en, output reg [7:0] data_in, input [7:0] data_out, output reg [7:0] addr, output reg start, output reg stop ); // Internal signals reg enable; reg r_w; reg [3:0] cnt_byte; // counter for bits in a byte (counts 0 to 8) reg [6:0] slave_addr; // 7-bit slave address received from master reg [7:0] reg_addr; // 8-bit register address (to select regA or regB) reg sda_out, sda_ack, sda_assign; // State machine declaration typedef enum reg [2:0] { IDLE = 3'b000, SLAVE_ADDR_READ = 3'b001, // Receive slave address (7-bit) + R/W bit ACK_SLAVE = 3'b010, // Acknowledge slave address reception REGADDR_READ = 3'b011, // Receive full 8-bit register address ACK_REG = 3'b100, // Acknowledge register address reception RW_CHECK = 3'b101, // (Optional) Check r/w bit (here merged in next state) ACK = 3'b110, // Acknowledge before data phase DATA = 3'b111 // Data transfer phase } state_t; state_t state, next_state; // Generate start condition: when sda falls with scl high always @(negedge sda or posedge rst) begin if (rst) start <= 0; else start <= (scl == 1); end // Generate stop condition: when sda rises with scl high always @(posedge sda or posedge rst) begin if (rst) stop <= 0; else stop <= (scl == 1); end // Enable signal based on start/stop events always @(start, stop, rst) begin if (rst) enable = 0; else if (start) enable = 1; else if (stop) enable = 0; else enable = enable; end // State register update always @(posedge clk or posedge rst) begin if (rst) state <= IDLE; else state <= next_state; end // Bit counter update always @(negedge scl or posedge rst) begin if (rst || start) cnt_byte <= 0; else if (state == SLAVE_ADDR_READ || state == REGADDR_READ) cnt_byte <= (cnt_byte == 4'd8) ? 0 : cnt_byte + 1; end // Bit reception: sample incoming bits on sda during SLAVE_ADDR_READ or REGADDR_READ always @(negedge scl or posedge rst) begin if (rst || start) begin slave_addr <= 7'd0; reg_addr <= 8'd0; r_w <= 0; end else begin if (state == SLAVE_ADDR_READ) begin // Bits 1 to 7 go into slave_addr (MSB first) if (cnt_byte >= 1 && cnt_byte <= 7) begin // Store bit (MSB first) slave_addr[7-cnt_byte] <= sda; end // Bit 8 is the R/W flag else if (cnt_byte == 8) begin r_w <= sda; end end else if (state == REGADDR_READ) begin if (cnt_byte >= 1 && cnt_byte <= 8) begin // Store bits into reg_addr (MSB first) reg_addr[8-cnt_byte] <= sda; end end end end // Assign register address to module output once received always @(*) begin if (rst) addr = 8'd0; else begin if (state == REGADDR_READ || state >= ACK_REG) addr = reg_addr; else addr = addr; end end // Next state and ACK generation logic always @(posedge scl or posedge rst) begin if (rst) begin next_state <= IDLE; sda_assign <= 0; sda_ack <= 0; wr_en <= 0; rd_en <= 0; data_in <= 8'd0; end else begin case (state) IDLE: begin next_state <= enable ? SLAVE_ADDR_READ : IDLE; sda_assign <= 0; wr_en <= 0; rd_en <= 0; end SLAVE_ADDR_READ: begin if (cnt_byte == 8) next_state <= ACK_SLAVE; else next_state <= SLAVE_ADDR_READ; end ACK_SLAVE: begin // Issue ACK for slave address reception sda_ack <= 1; next_state <= REGADDR_READ; end REGADDR_READ: begin if (cnt_byte == 8) next_state <= ACK_REG; else next_state <= REGADDR_READ; end ACK_REG: begin // Issue ACK for register address reception sda_ack <= 1; next_state <= RW_CHECK; end RW_CHECK: begin // Optionally check r_w value here; then proceed next_state <= ACK; end ACK: begin // Transition into data phase next_state <= DATA; end DATA: begin // In data phase, remain until a stop condition is detected if (stop) next_state <= IDLE; else next_state <= DATA; end default: next_state <= IDLE; endcase end end // Data Reception for write operations (master writes data to slave) // cnt_data counts bits in a data byte. reg [3:0] cnt_data; always @(negedge scl or posedge rst) begin if (rst || start) cnt_data <= 0; else if (state == DATA && r_w == 0) begin cnt_data <= (cnt_data == 4'd8) ? 0 : cnt_data + 1; case (cnt_data) 4'd1: data_in[7] <= sda; 4'd2: data_in[6] <= sda; 4'd3: data_in[5] <= sda; 4'd4: data_in[4] <= sda; 4'd5: data_in[3] <= sda; 4'd6: data_in[2] <= sda; 4'd7: data_in[1] <= sda; 4'd8: data_in[0] <= sda; endcase if (cnt_data == 8) wr_en <= 1; else wr_en <= 0; end end // Data Transmission for read operations (slave sends data to master) reg [3:0] cnt_data_read; always @(negedge scl or posedge rst) begin if (rst || start) cnt_data_read <= 0; else if (state == DATA && r_w == 1) begin cnt_data_read <= (cnt_data_read == 4'd8) ? 0 : cnt_data_read + 1; case (cnt_data_read) 4'd1: sda_out <= data_out[7]; 4'd2: sda_out <= data_out[6]; 4'd3: sda_out <= data_out[5]; 4'd4: sda_out <= data_out[4]; 4'd5: sda_out <= data_out[3]; 4'd6: sda_out <= data_out[2]; 4'd7: sda_out <= data_out[1]; 4'd8: sda_out <= data_out[0]; endcase rd_en <= (cnt_data_read == 0) ? 1 : 0; end end // sda-drivers: drive sda during ACK and read phases always @(*) begin case (state) ACK_SLAVE, ACK_REG: sda_assign = sda_ack; DATA: begin if (r_w == 1) sda_assign = sda_out; else sda_assign = sda; // in write mode, let master drive sda end default: sda_assign = sda_temp; endcase end assign sda = ((state == ACK_SLAVE) || (state == ACK_REG) || (state == DATA && r_w == 1)) ? sda_assign : sda_temp; endmodule
Verilog
module register( input clk, input rst, input wr_en, input rd_en, input [7:0] addr, input [7:0] data_in, output reg [7:0] data_out ); reg [7:0] regA; reg [7:0] regB; always @(posedge clk or posedge rst) begin if (rst) regA <= 8'd0; else if (wr_en && addr == 8'd5) regA <= data_in; end always @(posedge clk or posedge rst) begin if (rst) regB <= 8'd0; else if (wr_en && addr == 8'd10) regB <= data_in; end always @(posedge clk or posedge rst) begin if (rst) data_out <= 8'b0; else if (rd_en) begin case (addr) 8'd5: data_out <= regA; 8'd10: data_out <= regB; default: data_out <= 8'b0; endcase end end endmodule
Verilog
module tb; reg clk, rst, scl; wire sda; reg sda_temp; // master drives sda when needed wire wr_en, rd_en; wire [7:0] data_in; wire [7:0] data_out; wire [7:0] addr; reg start, stop; // Instantiate the I2C slave module i2c_slave slave ( .clk(clk), .rst(rst), .sda(sda), .scl(scl), .sda_temp(sda_temp), .wr_en(wr_en), .rd_en(rd_en), .addr(addr), .data_in(data_in), .data_out(data_out), .start(start), .stop(stop) ); // Instantiate the register bank module register regi ( .clk(clk), .rst(rst), .wr_en(wr_en), .rd_en(rd_en), .addr(addr), .data_in(data_in), .data_out(data_out) ); // Clock generation always #5 clk = ~clk; initial begin scl = 1; start = 0; stop = 0; sda_temp = 1; clk = 0; rst = 0; #2; rst = 1; #2; rst = 0; repeat(2) @(posedge clk); // --- Begin I2C transaction --- // Generate start condition sda_temp = 0; start = 1; @(posedge clk); scl = 0; start = 0; // --- Transmit slave address byte (7'd8 + R/W=0) --- // 7'd8 in binary = 0001000; transmitted MSB first, // followed by R/W=0: full byte = 00010000 // Bit 7: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 6: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 5: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 4: @(posedge clk); scl = 1; sda_temp = 1; // 1 -> this forms the MSB of 8: 0001000 @(posedge clk); scl = 0; // Bit 3: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 2: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 1: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 0 (R/W bit): @(posedge clk); scl = 1; sda_temp = 0; // 0 for write @(posedge clk); scl = 0; // (Simulate slave ACK here by letting slave drive sda; no action required from master) @(posedge clk); // --- Transmit register address byte (for example, 8'd5) --- // 8'd5 = 00000101 // Bit 7: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 6: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 5: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 4: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 3: @(posedge clk); scl = 1; sda_temp = 1; // 1 (value 4) @(posedge clk); scl = 0; // Bit 2: @(posedge clk); scl = 1; sda_temp = 0; // 0 @(posedge clk); scl = 0; // Bit 1: @(posedge clk); scl = 1; sda_temp = 1; // 1 (value 2) @(posedge clk); scl = 0; // Bit 0: @(posedge clk); scl = 1; sda_temp = 0; // 0 (value 1 is 0) @(posedge clk); scl = 0; // (Simulate slave ACK for register address) @(posedge clk); // --- Transmit data byte to write into the selected register --- // For example, data = 8'd55; 55 = 00110111 // Bit 7: @(posedge clk); scl = 1; sda_temp = 0; @(posedge clk); scl = 0; // Bit 6: @(posedge clk); scl = 1; sda_temp = 0; @(posedge clk); scl = 0; // Bit 5: @(posedge clk); scl = 1; sda_temp = 1; @(posedge clk); scl = 0; // Bit 4: @(posedge clk); scl = 1; sda_temp = 1; @(posedge clk); scl = 0; // Bit 3: @(posedge clk); scl = 1; sda_temp = 0; @(posedge clk); scl = 0; // Bit 2: @(posedge clk); scl = 1; sda_temp = 1; @(posedge clk); scl = 0; // Bit 1: @(posedge clk); scl = 1; sda_temp = 1; @(posedge clk); scl = 0; // Bit 0: @(posedge clk); scl = 1; sda_temp = 1; @(posedge clk); scl = 0; // (Simulate slave ACK for data byte) @(posedge clk); // --- Generate stop condition --- sda_temp = 1; stop = 1; @(posedge clk); scl = 1; stop = 0; repeat(2) @(posedge clk); $finish; end endmodule
SLAVE_ADDR_READ and issues an ACK (ACK_SLAVE).REGADDR_READ with an ACK (ACK_REG).DATA), where data is either received (write) or transmitted (read) based on the R/W flag.Welcome 👋
Flux helps you build PCBs faster with an AI teammate!
Create your account to collaborate, stay updated, fork your own version, and get instant answers from our AI agent.