I2C Byte Transmitter
Every I2C master controller must manage the bidirectional Serial Data (sda) line. Because the bus is open-drain, the master cannot actively drive a logic high; it must release the line and rely on a pull-up resistor. After transmitting an 8-bit payload, the master must release the bus entirely during the ninth clock cycle to allow the receiving slave to pull the line low, indicating an Acknowledge (ACK).
The module acts as the byte-level transmission engine. When start is asserted, it captures tx_data. Over the next eight clock cycles, it outputs the bits MSB-first on sda_out. To represent the open-drain nature, sda_out is driven to 0 to transmit a logic 0, and driven to 1 to release the bus for a logic 1. On the ninth cycle, it releases the bus (sda_out = 1) and reads sda_in. If sda_in is 1 during this cycle, a Not Acknowledge (NACK) occurred. On the tenth cycle, the module pulses done high for one cycle, outputs the NACK status on ack_error, and returns to idle.
The clk input is positive-edge triggered. The rst_n input is an asynchronous active-low reset. Upon reset, sda_out defaults to 1 (bus released), while busy, done, and ack_error default to 0. If start is asserted while busy is 1, it must be ignored. The ack_error signal must remain stable and valid during the exact cycle done is 1, and reset to 0 otherwise.
Cycle 1: rst_n=0 → sda_out=1, busy=0, done=0 Cycle 2: rst_n=1, start=1, tx_data=8'hC3 (11000011) → sda_out=1, busy=0, done=0 Cycle 3: start=0 → sda_out=1, busy=1 (Bit 7) Cycle 4: start=0 → sda_out=1, busy=1 (Bit 6) Cycle 5: start=0 → sda_out=0, busy=1 (Bit 5) Cycle 6: start=0 → sda_out=0, busy=1 (Bit 4) Cycle 7: start=0 → sda_out=0, busy=1 (Bit 3) Cycle 8: start=0 → sda_out=0, busy=1 (Bit 2) Cycle 9: start=0 → sda_out=1, busy=1 (Bit 1) Cycle 10: start=0 → sda_out=1, busy=1 (Bit 0) Cycle 11: sda_in=0 (Slave ACK) → sda_out=1, busy=1 (ACK cycle) Cycle 12: sda_in=0 → sda_out=1, busy=0, done=1, ack_error=0 Cycle 13: sda_in=0 → sda_out=1, busy=0, done=0, ack_error=0
flowchart LR
RESET(( )) -->|reset| IDLE
IDLE((IDLE)) -->|start=1| SHIFT
IDLE -->|start=0| IDLE
SHIFT((SHIFT)) -->|bit_count < 7| SHIFT
SHIFT -->|bit_count == 7| ACK_READ
ACK_READ((ACK_READ)) --> DONE
DONE(["DONE ★"]):::out --> IDLE
classDef out fill:#6C5CE7,stroke:#5B4FE8,color:#fff{ "signal": [
{ "name": "clk", "wave": "p............" },
{ "name": "rst_n", "wave": "01..........." },
{ "name": "start", "wave": "010.........." },
{ "name": "tx_data", "wave": "x=.........x.", "data": ["8'hC3"] },
{ "name": "sda_in", "wave": "xxxxxxxxxx0xx" },
{ "name": "sda_out", "wave": "1...0...1...." },
{ "name": "busy", "wave": "0.1........0." },
{ "name": "done", "wave": "0..........10" },
{ "name": "ack_error", "wave": "0............" }
], "head": { "text": "Transmission of 8'hC3 followed by a successful ACK." } }| Signal | Direction | Width | Description | |--------|-----------|-------|-------------| | clk | input | 1 | Positive-edge triggered clock | | rst_n | input | 1 | Asynchronous active-low reset; all outputs reset to 0 except sda_out which resets to 1 | | start | input | 1 | One-cycle pulse to initiate transmission | | tx_data | input | 8 | Byte to transmit; captured when start is 1 | | sda_in | input | 1 | Current state of the SDA bus; driven by the slave | | sda_out | output | 1 | Driven state of the SDA bus; 1 releases the bus, 0 pulls it low | | busy | output | 1 | High during the 9-cycle transmission phase | | done | output | 1 | Pulses high for one cycle when transmission completes | | ack_error | output | 1 | High if sda_in is 1 during the ACK cycle; valid only when done is 1 |
Constraints
- The
clkinput is positive-edge triggered. - The
rst_ninput is asynchronous and active-low. - On reset,
sda_outmust be 1;busy,done, andack_errormust be 0. - Data is transmitted MSB first.
- The module must ignore
startpulses ifbusyis 1. - The
ack_errorsignal must reset to 0 on the cycle afterdoneis asserted. - During the ACK cycle (the 9th active cycle of transmission),
sda_outmust be 1 to release the bus.
Topics
Solve this problem
Write the module in Verilog, SystemVerilog or VHDL. Your submission is compiled and simulated against a real testbench — you get the waveform back, not a stored answer.
This problem is part of Codiode Pro. The statement above is free to read.
The circuit builder and code editor need a desktop screen. On a phone, read the problem here and open it on a laptop to solve.