Skip to content

Commit 3613a4e

Browse files
committed
Add I2S module
1 parent b348763 commit 3613a4e

13 files changed

+923
-0
lines changed

i2s/README.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
### I2S Master
2+
3+
Github: [http://github.com/ultraembedded/cores](https://github.com/ultraembedded/cores/tree/master/i2s)
4+
5+
This is a simple I2S master module written in Verilog.
6+
7+
The module requires clock source which is used to derive MCLK and BCLK for the I2S interface.
8+
9+
The audio_clk_i clock rate should be:
10+
* 32KHz - 16.384MHz
11+
* 44.1KHz - 22.5792MHz
12+
* 48KHz - 24.576MHz
13+
14+
The frequency of clk_i must be more than 2 x audio_clk_i frequency.
15+
16+
The input interface expects 32-bits (2 x 16-bit audio samples) to be provided to it on 'sample_i' and held until 'sample_req_o' is pulsed (data pop request).
17+
18+
This allows connection to a simple FIFO for audio samples.
19+
20+
##### Testing
21+
22+
The supplied testbench requires the SystemC libraries and Icarus Verilog, both of which are available for free.

i2s/rtl/i2s.v

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//-----------------------------------------------------------------
2+
// I2S Master
3+
// V0.1
4+
// Ultra-Embedded.com
5+
// Copyright 2012
6+
//
7+
8+
//
9+
// License: GPL
10+
// If you would like a version with a more permissive license for
11+
// use in closed source commercial applications please contact me
12+
// for details.
13+
//-----------------------------------------------------------------
14+
//
15+
// This file is open source HDL; you can redistribute it and/or
16+
// modify it under the terms of the GNU General Public License as
17+
// published by the Free Software Foundation; either version 2 of
18+
// the License, or (at your option) any later version.
19+
//
20+
// This file is distributed in the hope that it will be useful,
21+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
22+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23+
// GNU General Public License for more details.
24+
//
25+
// You should have received a copy of the GNU General Public
26+
// License along with this file; if not, write to the Free Software
27+
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28+
// USA
29+
//-----------------------------------------------------------------
30+
module i2s
31+
(
32+
// Main clock (min 2x audio_clk_i)
33+
input clk_i,
34+
input rst_i,
35+
36+
// Audio clock (MCLK x 2):
37+
// For 44.1KHz: 22.5792MHz
38+
// For 48KHz: 24.576MHz
39+
input audio_clk_i,
40+
input audio_rst_i,
41+
42+
// I2S DAC Interface
43+
output i2s_mclk_o,
44+
output i2s_bclk_o,
45+
output i2s_ws_o,
46+
output i2s_data_o,
47+
48+
// Audio interface (16-bit x 2 = RL)
49+
// (synchronous to clk_i)
50+
input [31:0] sample_i,
51+
output sample_req_o
52+
);
53+
54+
//-----------------------------------------------------------------
55+
// Registers
56+
//-----------------------------------------------------------------
57+
reg [4:0] bit_count_q;
58+
59+
// Registered audio input data
60+
reg [31:0] sample_q;
61+
62+
// Xilinx: Place output flop in IOB
63+
//synthesis attribute IOB of mclk_q is "TRUE"
64+
//synthesis attribute IOB of ws_q is "TRUE"
65+
//synthesis attribute IOB of bclk_q is "TRUE"
66+
//synthesis attribute IOB of data_q is "TRUE"
67+
reg mclk_q;
68+
reg bclk_q;
69+
reg ws_q;
70+
reg data_q;
71+
72+
reg sample_req_q;
73+
reg next_data_q;
74+
75+
//-----------------------------------------------------------------
76+
// MCLK
77+
//-----------------------------------------------------------------
78+
reg [7:0] clock_div_q;
79+
80+
always @(posedge audio_clk_i or posedge audio_rst_i)
81+
if (rst_i)
82+
begin
83+
mclk_q <= 1'b0;
84+
clock_div_q <= 8'b0;
85+
end
86+
else
87+
begin
88+
mclk_q <= !mclk_q;
89+
clock_div_q <= clock_div_q + 8'd1;
90+
end
91+
92+
reg clk_en0_ms_q;
93+
reg clk_en1_q;
94+
reg clk_en2_q;
95+
96+
// Resync clk enable pulse to clk_i domain
97+
always @(posedge clk_i or posedge rst_i)
98+
if (rst_i)
99+
begin
100+
clk_en0_ms_q <= 1'b0;
101+
clk_en1_q <= 1'b0;
102+
clk_en2_q <= 1'b0;
103+
end
104+
else
105+
begin
106+
clk_en0_ms_q <= (clock_div_q == 8'd0);
107+
clk_en1_q <= clk_en0_ms_q;
108+
clk_en2_q <= clk_en1_q;
109+
end
110+
111+
// BCLK is div256 of MCLK
112+
wire bclk_en_w = !clk_en2_q && clk_en1_q;
113+
114+
//-----------------------------------------------------------------
115+
// I2S Output Generator
116+
//-----------------------------------------------------------------
117+
always @(posedge clk_i or posedge rst_i)
118+
begin
119+
if (rst_i == 1'b1)
120+
begin
121+
sample_q <= 32'b0;
122+
bit_count_q <= 5'd0;
123+
data_q <= 1'b0;
124+
ws_q <= 1'b0;
125+
bclk_q <= 1'b0;
126+
next_data_q <= 1'b0;
127+
sample_req_q <= 1'b0;
128+
end
129+
else if (bclk_en_w)
130+
begin
131+
// BCLK 1->0 - Falling Edge
132+
if (bclk_q)
133+
begin
134+
bclk_q <= 1'b0;
135+
136+
data_q <= next_data_q;
137+
next_data_q <= sample_q[5'd31 - bit_count_q];
138+
139+
// Word select
140+
ws_q <= bit_count_q[4];
141+
142+
// Increment bit position counter
143+
bit_count_q <= bit_count_q + 5'd1;
144+
end
145+
// BCLK 0->1 - Rising Edge
146+
else
147+
begin
148+
bclk_q <= 1'b1;
149+
150+
// Last bit in first half, buffer remainder and pop word
151+
if (bit_count_q == 5'd0)
152+
begin
153+
sample_q <= {sample_i[15:0], sample_i[31:16]};
154+
sample_req_q <= 1'b1;
155+
end
156+
end
157+
end
158+
else
159+
sample_req_q <= 1'b0;
160+
end
161+
162+
//-----------------------------------------------------------------
163+
// I2S DAC Interface
164+
//-----------------------------------------------------------------
165+
assign i2s_mclk_o = mclk_q;
166+
assign i2s_ws_o = ws_q;
167+
assign i2s_bclk_o = bclk_q;
168+
assign i2s_data_o = data_q;
169+
170+
assign sample_req_o = sample_req_q;
171+
172+
endmodule

i2s/testbench/i2s_decoder.cpp

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "i2s_decoder.h"
2+
3+
//-----------------------------------------------------------------
4+
// input: Handle rx data
5+
//-----------------------------------------------------------------
6+
void i2s_decoder::input(void)
7+
{
8+
do
9+
{
10+
wait();
11+
}
12+
while (rst_i.read());
13+
14+
bool first = true;
15+
int bit_cnt = 0;
16+
sc_uint <I2S_MAX_BITS> data = 0;
17+
sc_uint <I2S_MAX_BITS> rev_data = 0;
18+
bool ws;
19+
20+
while (true)
21+
{
22+
if (!first)
23+
{
24+
// Left
25+
if (!ws)
26+
{
27+
sc_assert(bit_cnt < m_bits);
28+
29+
rev_data[bit_cnt] = i2s_data_i.read();
30+
31+
if (++bit_cnt == m_bits)
32+
{
33+
for (int i=0;i<m_bits;i++)
34+
data[m_bits-i-1] = rev_data[i];
35+
36+
m_rx_fifo.write(data);
37+
data = 0;
38+
}
39+
}
40+
// Right
41+
else
42+
{
43+
sc_assert(bit_cnt >= m_bits);
44+
sc_assert(bit_cnt < (m_bits*2));
45+
46+
rev_data[bit_cnt-m_bits] = i2s_data_i.read();
47+
48+
if (++bit_cnt == (m_bits*2))
49+
{
50+
for (int i=0;i<m_bits;i++)
51+
data[m_bits-i-1] = rev_data[i];
52+
53+
m_rx_fifo.write(data);
54+
data = 0;
55+
bit_cnt = 0;
56+
}
57+
}
58+
}
59+
else
60+
first = false;
61+
62+
ws = i2s_ws_i.read();
63+
64+
wait();
65+
}
66+
}

i2s/testbench/i2s_decoder.h

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef I2S_DECODER_H
2+
#define I2S_DECODER_H
3+
4+
#include <systemc.h>
5+
6+
#define I2S_MAX_BITS 24
7+
8+
//-------------------------------------------------------------
9+
// i2s_decoder: Decoder for I2S interface
10+
//-------------------------------------------------------------
11+
SC_MODULE (i2s_decoder)
12+
{
13+
public:
14+
// Clock and Reset
15+
sc_in <bool> clk_i;
16+
sc_in <bool> rst_i;
17+
18+
// I/O
19+
sc_in <bool> i2s_mclk_i;
20+
sc_in <bool> i2s_bclk_i;
21+
sc_in <bool> i2s_ws_i;
22+
sc_in <bool> i2s_data_i;
23+
24+
// Constructor
25+
SC_HAS_PROCESS(i2s_decoder);
26+
i2s_decoder(sc_module_name name): sc_module(name),
27+
m_rx_fifo(1024)
28+
{
29+
m_bits = 16;
30+
SC_CTHREAD(input, i2s_bclk_i.pos());
31+
}
32+
33+
public:
34+
sc_uint <I2S_MAX_BITS> read(void) { return m_rx_fifo.read(); }
35+
bool read_ready(void) { return m_rx_fifo.num_available() > 0; }
36+
void set_bit_width(int bits) { m_bits = bits; }
37+
38+
private:
39+
void input(void);
40+
41+
sc_fifo < sc_uint<I2S_MAX_BITS> > m_rx_fifo;
42+
int m_bits;
43+
};
44+
45+
#endif

i2s/testbench/i2s_driver.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "i2s_driver.h"
2+
3+
//-----------------------------------------------------------------
4+
// output: Drive tx data
5+
//-----------------------------------------------------------------
6+
void i2s_driver::output(void)
7+
{
8+
wait();
9+
sc_assert(m_tx_fifo.num_available() > 0);
10+
11+
while (true)
12+
{
13+
sample_data_o.write(m_tx_fifo.read());
14+
15+
wait();
16+
17+
while (!sample_req_i.read())
18+
wait();
19+
}
20+
}

i2s/testbench/i2s_driver.h

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef I2S_DRIVER_H
2+
#define I2S_DRIVER_H
3+
4+
#include <systemc.h>
5+
6+
//-------------------------------------------------------------
7+
// i2s_driver:
8+
//-------------------------------------------------------------
9+
SC_MODULE (i2s_driver)
10+
{
11+
public:
12+
// Clock and Reset
13+
sc_in <bool> clk_i;
14+
sc_in <bool> rst_i;
15+
16+
// I/O
17+
sc_in <bool> sample_req_i;
18+
sc_out <sc_uint<32> > sample_data_o;
19+
20+
// Constructor
21+
SC_HAS_PROCESS(i2s_driver);
22+
i2s_driver(sc_module_name name): sc_module(name),
23+
m_tx_fifo(2048)
24+
{
25+
SC_CTHREAD(output, clk_i.pos());
26+
}
27+
28+
public:
29+
void write(sc_uint <32> data) { m_tx_fifo.write(data); }
30+
bool write_empty(void) { return m_tx_fifo.num_available() == 0; }
31+
32+
private:
33+
void output(void);
34+
35+
sc_fifo < sc_uint<32> > m_tx_fifo;
36+
};
37+
38+
#endif

0 commit comments

Comments
 (0)