From 92dd9370ad2760e25fd175cd4342a4d3292bebcd Mon Sep 17 00:00:00 2001 From: Imants Pulkstenis Date: Sun, 13 Oct 2019 00:30:42 +0300 Subject: [PATCH] i2s rx tx --- Basys3_Master.xdc | 54 +++++++++++------------ i2s_transceiver.v | 107 ++++++++++++++++++++++++++++++++++++++++++---- top.v | 91 ++++++++++++++++++++++++--------------- 3 files changed, 182 insertions(+), 70 deletions(-) diff --git a/Basys3_Master.xdc b/Basys3_Master.xdc index 7e4df19..17d69bb 100644 --- a/Basys3_Master.xdc +++ b/Basys3_Master.xdc @@ -108,8 +108,8 @@ set_property PACKAGE_PIN W5 [get_ports clk] ##Buttons -#set_property PACKAGE_PIN U18 [get_ports btnC] - #set_property IOSTANDARD LVCMOS33 [get_ports btnC] +set_property PACKAGE_PIN U18 [get_ports btnC] + set_property IOSTANDARD LVCMOS33 [get_ports btnC] #set_property PACKAGE_PIN T18 [get_ports btnU] #set_property IOSTANDARD LVCMOS33 [get_ports btnU] #set_property PACKAGE_PIN W19 [get_ports btnL] @@ -121,31 +121,31 @@ set_property PACKAGE_PIN W5 [get_ports clk] -##Pmod Header JA -##Sch name = JA1 -#set_property PACKAGE_PIN J1 [get_ports {JA[0]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[0]}] -##Sch name = JA2 -#set_property PACKAGE_PIN L2 [get_ports {JA[1]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[1]}] -##Sch name = JA3 -#set_property PACKAGE_PIN J2 [get_ports {JA[2]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[2]}] -##Sch name = JA4 -#set_property PACKAGE_PIN G2 [get_ports {JA[3]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[3]}] -##Sch name = JA7 -#set_property PACKAGE_PIN H1 [get_ports {JA[4]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[4]}] -##Sch name = JA8 -#set_property PACKAGE_PIN K2 [get_ports {JA[5]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[5]}] -##Sch name = JA9 -#set_property PACKAGE_PIN H2 [get_ports {JA[6]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[6]}] -##Sch name = JA10 -#set_property PACKAGE_PIN G3 [get_ports {JA[7]}] - #set_property IOSTANDARD LVCMOS33 [get_ports {JA[7]}] +#Pmod Header JA +#Sch name = JA1 +set_property PACKAGE_PIN J1 [get_ports {da_mclk}] + set_property IOSTANDARD LVCMOS33 [get_ports {da_mclk}] +#Sch name = JA2 +set_property PACKAGE_PIN L2 [get_ports {da_lrck}] + set_property IOSTANDARD LVCMOS33 [get_ports {da_lrck}] +#Sch name = JA3 +set_property PACKAGE_PIN J2 [get_ports {da_sclk}] + set_property IOSTANDARD LVCMOS33 [get_ports {da_sclk}] +#Sch name = JA4 +set_property PACKAGE_PIN G2 [get_ports {da_sdin}] + set_property IOSTANDARD LVCMOS33 [get_ports {da_sdin}] +#Sch name = JA7 +set_property PACKAGE_PIN H1 [get_ports {ad_mclk}] + set_property IOSTANDARD LVCMOS33 [get_ports {ad_mclk}] +#Sch name = JA8 +set_property PACKAGE_PIN K2 [get_ports {ad_lrck}] + set_property IOSTANDARD LVCMOS33 [get_ports {ad_lrck}] +#Sch name = JA9 +set_property PACKAGE_PIN H2 [get_ports {ad_sclk}] + set_property IOSTANDARD LVCMOS33 [get_ports {ad_sclk}] +#Sch name = JA10 +set_property PACKAGE_PIN G3 [get_ports {ad_sdout}] + set_property IOSTANDARD LVCMOS33 [get_ports {ad_sdout}] diff --git a/i2s_transceiver.v b/i2s_transceiver.v index 8567b74..d4e70cc 100644 --- a/i2s_transceiver.v +++ b/i2s_transceiver.v @@ -9,19 +9,108 @@ // e.g. word select, are synonymous.) // The generic parameter mclk_sclk_ratio is set to 4, // since MCLK/SCLK = (MCLK/LRCK) / (SCLK/LRCK) = 256/64 = 4. +// +// As such, the word select (or left-right clock) frequency +// is 44.1 kHz, the serial clock frequency is 44.1 kHz * 64 = 2.82 MHz, +// and the master clock frequency is 2.82 MHz * 4 = 11.29 MHz. +// Table 1, Section 4.1 of the CS4344 Datasheet confirms this +// selection, listing 11.29 MHz as a common frequency +// for the master clock when the LRCK is 44.1 kHz. - -module i2c_transceiver #( parameter - sclk_ws_ratio = 64, - mclk_sclk_ratio = 4 +module i2s_transceiver #( parameter + sclk_ws_ratio = 64, // number of sclk periods per word select period + mclk_sclk_ratio = 4, // number of mclk periods per sclk period + d_width = 24 // data width )( - output sclk, - input mclk, - output ws, - input sd_rx, - output sd_tx ); + input reset_n, //asynchronous active low reset + input mclk, //master clock + output sclk, //serial clock (or bit clock) + output ws, //word select (or left-right clock) + output sd_tx, //serial data transmit + input sd_rx, //serial data receive + input [d_width-1: 0] l_data_tx, //left channel data to transmit + input [d_width-1: 0] r_data_tx, //right channel data to transmit + output [d_width-1: 0] l_data_rx, //left channel data received + output [d_width-1: 0] r_data_rx //right channel data received + + ); + +reg sclk_int = 0; //internal serial clock wire +reg ws_int = 0; //internal word select wire +reg [d_width-1: 0] l_data_rx_int = 0; //internal left channel rx data buffer +reg [d_width-1: 0] r_data_rx_int = 0; //internal right channel rx data buffer +reg [d_width-1: 0] l_data_tx_int = 0; //internal left channel tx data buffer +reg [d_width-1: 0] r_data_tx_int = 0; //internal right channel tx data buffer + +reg r_sd_tx = 0; //internal register +reg [d_width-1: 0] reg_r_data_rx = 0; +reg [d_width-1: 0] reg_l_data_rx = 0; +reg [d_width-1: 0] reg_r_data_tx = 0; +reg [d_width-1: 0] reg_l_data_tx = 0; + +reg sclk_cnt = 0; //counter of master clocks during half period of serial clock +reg ws_cnt = 0; //counter of serial clock toggles during half period of word select +always@(mclk || reset_n) begin + + if (reset_n == 0) begin + sclk_cnt <= 0; //clear mclk/sclk counter + ws_cnt <= 0; //clear sclk/ws counter + sclk_int <= 0; //clear serial clock signal + ws_int <= 0; //clear word select signal + l_data_rx_int <= 'b0; //clear internal left channel rx data buffer + r_data_rx_int <= 'b0; //clear internal right channel rx data buffer + l_data_tx_int <= 'b0; //clear internal left channel tx data buffer + r_data_tx_int <= 'b0; //clear internal right channel tx data buffer + r_sd_tx <= 0; //clear serial data transmit output + reg_l_data_rx <= 'b0; //clear left channel received data output + reg_r_data_rx <= 'b0; //clear right channel received data output + end + else if (mclk == 1) begin //master clock rising edge + if (sclk_cnt < mclk_sclk_ratio/2-1) begin //less than half period of sclk + sclk_cnt <= sclk_cnt + 1; //increment mclk/sclk counter + end + else begin //half period of sclk + sclk_cnt <= 0; //reset mclk/sclk counter + sclk_int <= ~sclk_int; //toggle serial clock + if (ws_cnt < sclk_ws_ratio - 1) begin //less than half period of ws + ws_cnt <= ws_cnt + 1; //increment sclk/ws counter + if (sclk_int == 0 && ws_cnt > 1 && ws_cnt < d_width * 2 + 2) begin //rising edge of sclk during data word + if (ws_int == 1) begin //right channel + r_data_rx_int <= {r_data_rx_int[d_width-2 : 0] , sd_rx}; //shift data bit into right channel rx data buffer + end else begin //left channel + l_data_rx_int <= {l_data_rx_int[d_width-2 : 0] , sd_rx}; //shift data bit into left channel rx data buffer + end + end + + if (sclk_int == 1 && ws_cnt > 1 && ws_cnt < d_width*2+3) begin //falling edge of sclk during data word + if (ws_int == 1) begin //right channel + r_sd_tx <= r_data_tx_int[d_width-1]; //transmit serial data bit + r_data_rx_int <= {r_data_rx_int[d_width-2 : 0] , 0}; //shift data of right channel tx data buffer + end else begin //left channel + r_sd_tx <= l_data_tx_int[d_width-1]; //ransmit serial data bit + l_data_rx_int <= {l_data_rx_int[d_width-2 : 0] , 0}; //shift data of left channel tx data buffer + end + end + end else begin //half period of ws + ws_cnt <= 0; //reset sclk/ws counter + ws_int <= ~ws_int; //toggle word select + reg_r_data_rx <= r_data_rx_int; //output right channel received data + reg_l_data_rx <= l_data_rx_int; //output left channel received data + r_data_tx_int <= r_data_tx; //latch in right channel data to transmit + l_data_tx_int <= l_data_tx; //latch in left channel data to transmit + end + end + end +end + +assign sclk = sclk_int; //output serial clock +assign ws = ws_int; //output word select +assign sd_tx = r_sd_tx; //assign sd_tx +assign r_data_rx = reg_r_data_rx; +assign l_data_rx = reg_l_data_rx; + endmodule \ No newline at end of file diff --git a/top.v b/top.v index afb85c6..4811d61 100644 --- a/top.v +++ b/top.v @@ -10,53 +10,76 @@ // Fs = I2SxCLK / [(CF*2) * (2*I2SDIV+ODD)*8)] // CF = channel frame (24 bits) // -// `include "clock_divider_param.v" -// `include "clock_enable_param.v" -// `include "debounce_switch.v" module top #( parameter - //enable signal parameters - WAIT = 1, - WAIT_WIDTH = 2 + sclk_ws_ratio = 64, // number of sclk periods per word select period + mclk_sclk_ratio = 4, // number of mclk periods per sclk period + d_width = 24 // data width )( - input clk + input clk, + input btnC, + output da_mclk, + output ad_mclk, + output da_sclk, + output ad_sclk, + output da_lrck, + output ad_lrck, + input ad_sdout, + output da_sdin ); //------internal wires and registers-------- -// wire w_enable; -wire clk48k; -wire clk25M; +wire master_clk; // 11.29 MHz master clock +wire serial_clk; +wire word_select; +wire reset_n; +wire [d_width-1: 0] r_data_tx; +wire [d_width-1: 0] l_data_tx; + +wire w_sd_tx; //internal wire //-----sub modules-------------------------- -// clock_enable_param #( -// .WAIT(WAIT), -// .WIDTH(WAIT_WIDTH) -// ) clock_enable1 ( -// .clk(clk), -// .enable(w_enable) -// ); -clock_divider #( - .DIVIDER(1042), - .WIDTH(11) -) clk48k_gen ( - .clk(clk), - .clk_out(clk48k) // 48kHz clock +//declare PLL to create 11.29 MHz master clock from 100 MHz system clock +clk_wiz_0 m_clk( + .clk_in1(clk), + .clk_out1(master_clk) ); -clock_divider #( - .DIVIDER(2), - .WIDTH(2) -) clk25M_gen ( - .clk(clk), - .clk_out(clk25M) // 25MHz clock +//instantiate I2S Transceiver component +i2s_transceiver #( + .mclk_sclk_ratio(mclk_sclk_ratio), //number of mclk periods per sclk period + .sclk_ws_ratio(sclk_ws_ratio), //number of sclk periods per word select period + .d_width(d_width) //data width +) i2s_transceiver ( + .reset_n(reset_n), //asynchronous active low reset + .mclk(master_clk), //master clock + .sclk(serial_clk), //serial clock (or bit clock) + .ws(word_select), //word select (or left-right clock) + .sd_rx(ad_sdout), //serial data transmit + .sd_tx(w_sd_tx), //serial data receive + .l_data_tx(l_data_tx), //left channel data to transmit + .r_data_tx(r_data_tx), //right channel data to transmit + .l_data_rx(l_data_tx), //left channel data received + .r_data_rx(r_data_tx) //right channel data received ); -// debounce_switch debounce_switch_reset( -// .clk(clk), -// .i_switch(), -// .o_switch() -// ); +//debounce reset button +debounce_switch debounce_switch_reset( + .clk(clk), + .i_switch(btnC), + .o_switch(reset_n) +); + +assign da_mclk = master_clk; //output master clock to ADC +assign ad_mclk = master_clk; //output master clock to DAC +assign da_sclk = serial_clk; //output serial clock (from I2S Transceiver) to ADC +assign ad_sclk = serial_clk; //output serial clock (from I2S Transceiver) to DAC +assign da_lrck = word_select; //output word select (from I2S Transceiver) to ADC +assign ad_lrck = word_select; //output word select (from I2S Transceiver) to DAC + +assign da_sdin = w_sd_tx; //assign right channel received data to transmit (to playback out received data) + endmodule \ No newline at end of file