CodiodeCodiode
Home
Problem Solving
Skill Tracks
My Assignments
Contests
Leaderboard
Community
Settings
Codiode/Problems/Sequential Logic

I2C Byte Transmitter

HardVerilog / SystemVerilogBuild

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 clk input is positive-edge triggered.
  • The rst_n input is asynchronous and active-low.
  • On reset, sda_out must be 1; busy, done, and ack_error must be 0.
  • Data is transmitted MSB first.
  • The module must ignore start pulses if busy is 1.
  • The ack_error signal must reset to 0 on the cycle after done is asserted.
  • During the ACK cycle (the 9th active cycle of transmission), sda_out must be 1 to release the bus.

Topics

fsmshift-registerprotocoli2c

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.

Sign in to solveSee what Pro unlocks

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.

Related problems

  • Basic D Flip FlopEasy
  • Debug: Missing Edge in Sensitivity ListMedium
  • Read After Write Hazard DetectionEasy
  • Parameterized Interface with ModportsHard
  • Struct Array PipelineHard
  • T Flip Flop from D Flip Flop TemplateEasy
  • Recursive Generate Reduction TreeHard
  • Four Stage Shift RegisterEasy

Browse all problems · Learning tracks