Interactive documentation
I2S AXI4-Lite IP, explained visually from clock source to serial data
Use the controls to change the clock family, divider mode, sample width, and runtime behaviour. Every derived value updates live so the register map, timing math, and I2S waveform stay connected.
What the core does
The software-visible CONTROL register chooses the family, mode, sample width, and run state. The audio side uses that selection to create MCLK, BCLK, WS, and the serial payload.
Real-time Visualization
Overall I2S Flow & FS Divide
This live animation shows the internal DDS logic. The accumulator adds the frequency step every system clock. When it overflows, BCLK toggles. DATA bits are shifted out MSB-first based on the bit counter.
Step Value
6,144,000
Architecture Evolution
Cycle Divider: Old vs. New Design
Compare how the clock was generated in the first version versus the latest DDS-optimized version.
⏱️ Fixed Integer Divider
// Counts 0, 1, 2, 3... 0
bclk_en = (div_count == 4'd3);
How it worked:
- Used a simple counter (0, 1, 2, 3).
- Fired exactly every 4 system cycles.
- Result: Fixed frequencies (44.1k or 48k).
- Limited: Hard to get non-standard frequencies.
🌊 DDS Phase Accumulator
// Accumulates water (step)
bclk_en = (acc >= 100MHz);
How it works now:
- Uses a 32-bit "Bucket" (Accumulator).
- Adds any "Step" value every cycle.
- Result: Arbitrary frequencies (1Hz to 1MHz).
- Flexible: Change Fs in real-time via software.
The Big Difference: The old design was like a gear that only fits specific teeth. The new DDS design is like a liquid flow that can fill the bucket at any speed, giving usInfinite Flexibility.
Section 0
One-Viewport Serializer Walkthrough
This panel follows the real datapath in order: AXI write, CDC, frame latch, WS delay bit, left-slot serialization, WS transition, and right-slot serialization. The motion now tracks the sample itself so you can see when it is still a parallel word, when it is frozen for a frame, and when it finally becomes a single serial DATA bit.
RTL focus
AXI register write
slv_reg0 <= S_AXI_WDATA; // CPU writes left samplecdc_left_stage1_q <= slv_reg0; // first sync flopcdc_left_stage2_q <= cdc_left_stage1_q; // second sync flopif (bclk_en_w && bit_count_q == 6'd0) sample_left_q <= cdc_left_stage2_q;i2s_ws_o <= bit_count_q[5]; // 0 for left slot, 1 for right slotif (bit_count_q == 6'd0 || bit_count_q == 6'd32) i2s_data_o <= 1'b0;else if (bit_count_q <= g_width) i2s_data_o <= sample_left_q[g_width - bit_count_q];else if (bit_count_q > 6'd32 && bit_count_q <= (6'd32 + g_width)) i2s_data_o <= sample_right_q[g_width - (bit_count_q - 6'd32)];else i2s_data_o <= 1'b0; // pad remaining slot bits with zeroWhy this step matters
The processor updates slv_reg0 immediately, but the serializer does not touch it yet.
This decouples software timing from the audio bitstream. A write can happen at any AXI cycle without corrupting an in-flight I2S word.
WS = 0, DATA = 0, and the active serializer position is bit_count 0. That state is what a logic analyzer or ILA would observe on this exact cycle.
The serializer never streams directly from AXI registers. It waits for a clean CDC transfer and a frame boundary, then emits one delay bit plus MSB-first payload for each channel.
Section 1
Clock generation
FS_FAMILY chooses the clock source before anything else happens. That source then feeds a toggle flop so the final MCLK is always exactly half of internal_mclk.
FS_FAMILY = 0 selects audio_48_clk from the BUFGMUX.
internal_mclk = 24.576 MHz.
mclk_q toggles every cycle, so MCLK = internal_mclk ÷ 2 = 12.288 MHz.
48 kHz family: 24.576 MHz master clock, then divide by 2 for MCLK.
That is the clock before the toggle flop divides it down.
The flip-flop changes state on each edge, so the output period spans two input cycles.
Exactly half of internal_mclk.
Section 2
Clock divider and BCLK enable
FS_MODE changes which low bits are inspected. That changes how often bclk_en_w fires, and the divider math then produces BCLK, WS, and Fs.
FS_MODE = 0 uses clock_div_q[1:0] == 2'b00, so the enable fires every 4 cycles.
bclk_en period = internal_mclk ÷ divider = 6.144 MHz.
BCLK = bclk_en_rate ÷ 2 = 3.072 MHz because one full clock needs both a rising and falling edge.
WS = BCLK ÷ 64 = 48.000 kHz and Fs = WS = 48.000 kHz.
| FS_FAMILY | FS_MODE | internal_mclk | divider | bclk_en rate | BCLK | WS | Fs |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 24.576 MHz | 4 | 6.144 MHz | 3.072 MHz | 48.0 kHz | 48.0 kHz |
| 0 | 1 | 24.576 MHz | 2 | 12.288 MHz | 6.144 MHz | 96.0 kHz | 96.0 kHz |
| 1 | 0 | 22.579 MHz | 4 | 5.645 MHz | 2.822 MHz | 44.1 kHz | 44.1 kHz |
| 1 | 1 | 22.579 MHz | 2 | 11.290 MHz | 5.645 MHz | 88.2 kHz | 88.2 kHz |
Section 3
Power-on reset
audio_por_q starts at 1111 and shifts a 0 in from the right each cycle. That makes audio_rst stay high for four cycles and then drop low cleanly in the audio clock domain.
The reset must be generated inside the audio clock domain, not copied directly from S_AXI_ARESETN, because the AXI reset belongs to a different clock domain.
At cycle 0 the register is 1111. By cycle 4 it becomes 0000 and audio_rst drops to 0.
Section 4
CDC synchroniser
A two-stage synchroniser carries a value from the AXI domain into the destination domain. The first flop samples it, the second flop exports it one more cycle later, which improves MTBF and reduces the chance of metastability escaping into the core.
Value is generated in the AXI/register world.
First flop captures the asynchronous transition.
Second flop exports a cleaner version into the destination domain.
The source value is launched in the AXI domain.
stage1_q captures the transition first, then stage2_q follows one cycle later.
Marking both flip-flops with ASYNC_REG=TRUE tells the tool to keep them adjacent so the routing is short and the synchroniser is more reliable.
Section 5
CONTROL register bitfield
The register is the software contract. Each field is clickable, and the default value 0x00001801 is annotated so you can see exactly which settings are active at reset, including reserved bits that must stay 0.
Selected field
ENABLE
Turns the audio engine on or off.
Effect on the audio path
When 0, BCLK, WS, and DATA are held low and the bit counter is reset.
Range and value
bit 0 · default 1
Section 6
Philips I2S frame structure
This is one full 64-bit stereo frame. The left channel occupies cells 0–31, the right channel occupies cells 32–63, and the sample width slider changes where payload ends and padding begins.
pos 0 and pos 32 are the always-zero delay cells. pos 1 is sample[width-1]. pos 2..width count down through the remaining payload bits. Everything after width is padding.
Always 0, creating the 1 BCLK delay after the WS edge required by Philips I2S.
sample[width-1], the MSB of the selected sample word.
sample[width-pos], continuing MSB-first until the payload is exhausted.
0, which is the padding region.
Placeholder
[PHOTO: ILA capture showing BCLK, WS, DATA for one full 64-bit stereo frame at 48 kHz]Use this slot for a real capture when you want to prove the frame on hardware with the same timing shown above.
Section 7
WS timing
bit_count_q[5] is the clean left/right split. It is 0 for bit_count 0..31 and 1 for bit_count 32..63, so WS changes once per half-frame while DATA changes on the falling BCLK edge.
WS timing
Live waveform preview
This preview tracks the 64-bit stereo frame and highlights the current bit_count_q slot.
bit_count 0..31 means bit[5] = 0, so WS = LOW.
bit_count 32..63 means bit[5] = 1, so WS = HIGH.
Section 8
ENABLE and MUTE behaviour
These toggles show what the output pins do in real time. ENABLE stops the whole audio engine, while MUTE keeps the clocks alive and only forces DATA low.
BCLK, WS, and DATA all stay low and bit_count resets to 0.
BCLK and WS continue normally, but DATA is forced to 0.
ENABLE = 1 and MUTE = 0 produce the full I2S stream.
Output behaviour
Live waveform preview
Watch the same frame preview respond instantly to ENABLE and MUTE.
Section 9
Full formula summary
This card ties the whole chain together. Pick a family and mode anywhere on the page, then read the selected path all the way down to Fs.
Fs = 48.0 kHz
divider 4 · BCLK 3.072 MHzFs = 96.0 kHz
divider 2 · BCLK 6.144 MHzFs = 44.1 kHz
divider 4 · BCLK 2.822 MHzFs = 88.2 kHz
divider 2 · BCLK 5.645 MHzPlaceholder
[PHOTO: Oscilloscope capture showing MCLK, BCLK, WS, and DATA aligned for one complete stereo frame]Use a hardware image here when you want the visual summary to match a real board capture.