2 lines
14 KiB
Plaintext
2 lines
14 KiB
Plaintext
|
|
{"task_id": "i2c_controller", "task_number": 2, "description": "Single-byte I2C master controller with an explicit FSM and open-drain scl/sda behavior. On start_tx, if the bus is idle high, generate START, send {addr,rw}, check ACK1, then either write one byte from data_in or read one byte from sda into data_out. For write mode, sample ACK2 after the data byte. During STOP, if scl is externally held low, stay in CLOCK_STRETCH until the line is released. If the controller releases sda for a logic-1 bit but observes sda low during address transmission, treat this as arbitration loss and set ack_err. Any NACK or illegal START condition should also set ack_err before returning to IDLE.", "header": "module i2c_master (\n input clk,\n input rst_n,\n input [6:0] addr,\n input rw,\n input [7:0] data_in,\n input start_tx,\n output reg [7:0] data_out,\n output reg busy,\n output reg ack_err,\n inout scl,\n inout sda\n);", "module_code": "`timescale 1ns/1ps\n\nmodule i2c_master (\n input clk,\n input rst_n,\n input [6:0] addr,\n input rw,\n input [7:0] data_in,\n input start_tx,\n output reg [7:0] data_out,\n output reg busy,\n output reg ack_err,\n inout scl,\n inout sda\n);\n localparam IDLE = 4'd0;\n localparam START_A = 4'd1;\n localparam START_B = 4'd2;\n localparam ADDR_DRIVE = 4'd3;\n localparam ADDR_SAMPLE = 4'd4;\n localparam ACK1 = 4'd5;\n localparam WRITE_DRIVE = 4'd6;\n localparam WRITE_SAMPLE = 4'd7;\n localparam READ = 4'd8;\n localparam ACK2 = 4'd9;\n localparam STOP_A = 4'd10;\n localparam STOP_B = 4'd11;\n localparam CLOCK_STRETCH = 4'd12;\n localparam ARBITRATION_LOST = 4'd13;\n localparam ERROR = 4'd14;\n\n reg [3:0] state;\n reg [2:0] bit_count;\n reg [7:0] tx_shift;\n reg [7:0] rx_shift;\n reg scl_drive_low;\n reg sda_drive_low;\n reg latched_rw;\n\n wire scl_in;\n wire sda_in;\n\n assign scl = scl_drive_low ? 1'b0 : 1'bz;\n assign sda = sda_drive_low ? 1'b0 : 1'bz;\n assign scl_in = scl;\n assign sda_in = sda;\n\n always @(posedge clk or negedge rst_n) begin\n if (!rst_n) begin\n state <= IDLE;\n bit_count <= 3'd0;\n tx_shift <= 8'd0;\n rx_shift <= 8'd0;\n scl_drive_low <= 1'b0;\n sda_drive_low <= 1'b0;\n latched_rw <= 1'b0;\n data_out <= 8'd0;\n busy <= 1'b0;\n ack_err <= 1'b0;\n end else begin\n case (state)\n IDLE: begin\n busy <= 1'b0;\n scl_drive_low <= 1'b0;\n sda_drive_low <= 1'b0;\n bit_count <= 3'd7;\n rx_shift <= 8'd0;\n if (start_tx) begin\n busy <= 1'b1;\n ack_err <= 1'b0;\n tx_shift <= {addr, rw};\n latched_rw <= rw;\n state <= START_A;\n end\n end\n\n START_A: begin\n busy <= 1'b1;\n if (scl_in && sda_in) begin\n sda_drive_low <= 1'b1;\n scl_drive_low <= 1'b0;\n state <= START_B;\n end else begin\n ack_err <= 1'b1;\n state <= ERROR;\n end\n end\n\n START_B: begin\n busy <= 1'b1;\n scl_drive_low <= 1'b1;\n bit_count <= 3'd7;\n sda_drive_low <= ~tx_shift[7];\n state <= ADDR_DRIVE;\n end\n\n ADDR_DRIVE: begin\n busy <= 1'b1;\n scl_drive_low <= 1'b0;\n sda_drive_low <= ~tx_shift[7];\n state <= ADDR
|