6.5 IIS接口控制器设计与源代码分析
6.5.1 IIS控制器框图
模块框图如图6-10所示。
图6-10 IIS接口模块框图
6.5.2 功能描述
支持的总线标准为WISHBONE rev B3。
PSRA为SCK分频率器,其值为时钟频率/音频采样频率/32-1,再取整数。目前IIS接口只支持最常见8比特音频,采样速率可自由选择,只受Audio Codec的限制。若改为16比特,只需将FIFO的宽度增加到32位即可。
PSRA为CDCLK分频率器,其值为8×(PSRA+1)-1。目前IIS接口只支持256fs CDCLK。一般Audio Codec都支持。对其他分频率的支持尚未验证。
TxFIFO和RxFIFO都是深度32,宽16。高8比特为左声道,低8比特为右声道。
L3mode针对uda1341a设计,不是IIS协议本身的内容。
IIS接口支持DMA和非DMA两种模式。DMA所设计模式为支持DemoSoC的DMA控制器而设计。
目前只支持标准IIS格式,如图6-11所示。不支持MSB-justfied模式。
图6-11 标准IIS格式时序
6.5.3 接口信号
IIS接口控制器接口信号如表6-1所示。
表6-1 IIS接口信号
6.5.4 典型速率
音频编解码器的工作时钟CODECCLK或CDCLK必须以256或384倍音频采样频率fs方式工作。表6-2列出了fs和CODECCLK的典型值。
表6-2 音频CODEC时钟(CODECLK = 256 or 384fs)
当左右声道采样比特均为8比特时,串行时钟频率fIISCLK=16fs;当左右声道采样比特均为16比特时,串行时钟频率fIISCLK=32fs;当左右声道采样比特均为24比特时,串行时钟频率fIISCLK=48fs,串行时钟IISCLK的典型值如表6-3所示。
表6-3 可用串行时钟频率(IISCLK = 16 or 32 or 48fs)
6.5.5 IIS总线接口寄存器
IIS总线接口寄存器定义及说明如表6-4~表6-9所示。
表6-4 IIS控制寄存器(IISCON)
表6-9 IIS L3 REGISTER
表6-5 IIS MODE REGISTER(IISMOD)REGISTER
表6-6 PRESCALER(IISPSR)REGISTER
表6-7 IIS FIFO CONTROL(IISFCON)REGISTER
表6-8 IIS FIFO(IISFIFO)REGISTER
6.5.6 设计文件列表
IIS控制器设计文件列表如表6-10所示。
表6-10 IIS控制器设计文件
6.5.7 源代码分析
`include "timescale.v" module iis_top(clk,rstn,wb_data_i,wb_data_o,wb_addr_i,wb_sel_i, wb_we_i,wb_cyc_i ,wb_stb_i, wb_ack_o,wb_err_o,wb_rty_o, ws,sck,datai,datao,cdclk, //IIS接口 l3mode,l3clock,l3data,// //uda1341ts L3控制接口 dma_req,dma_ack//DMA请求接口 ); //寄存器地址和数据宽度定义,IIS接口寄存器的定义见前文 parameter WB_WIDTH=32; parameter IISCON_ADDR = 6'd0; parameter IISMOD_ADDR = 6'd4; parameter IISPSR_ADDR = 6'd8; parameter IISFCON_ADDR = 6'd12; parameter IISFIFO_ADDR = 6'd16; parameter IISL3_ADDR = 6'd20; ……//此处忽略输入输出信号 output reg [1:0] dma_req; //两个DMA通道,一个音频输入,一个音频输出 input [1:0] dma_ack; //寄存器操作逻辑1.IISCON wire wb_cyc_stb=wb_cyc_i & wb_stb_i; wire tx_fifo_full,tx_fifo_empty; wire rx_fifo_full,rx_fifo_empty; reg [31:0] rx_fifo_data; reg [5:0] iiscon; wire tx_fifo_rdy=~tx_fifo_empty; wire rx_fifo_rdy =~tx_fifo_full; wire iiscon_rd={tx_fifo_rdy,rx_fifo_rdy,iiscon}; wire tx_dma_req_ena = iiscon[5]; wire rx_dma_req_ena = iiscon[4]; wire tx_chl_idle_cmd = iiscon[3]; wire rx_chl_idle_cmd = iiscon[2]; wire prescaler_ena = iiscon[1]; wire iis_ena = iiscon[0]; wire sel_iiscon = (wb_addr_i==IISCON_ADDR) & (|wb_sel_i) & (wb_cyc_stb); always @( posedge clk or negedge rstn) begin if(~rstn) iiscon<=0; else if(sel_iiscon & wb_we_i & wb_ack_o) begin iiscon[5:0] <=wb_data_i[5:0]; end end //1.IIS模式寄存器 reg [7:0] iismod; wire [1:0] transfer_mode = iismod [7:6]; wire active_level_left = iismod[5]; wire serial_if_format = iismod[4]; wire serial_bit_per_chl= iismod[3]; wire master_clk_fre =iismod[2]; wire [1:0] sck_fre_sel = iismod[1:0]; wire sel_iismod = (wb_addr_i==IISMOD_ADDR) & (|wb_sel_i) & (wb_cyc_stb); always @( posedge clk or negedge rstn) begin if(~rstn) iismod<=8'hc0;//default to receive & transmit else if(sel_iismod & wb_we_i & wb_ack_o) begin iismod[7:0] <=wb_data_i[7:0]; end end //2.IISPSR reg [31:0] iispsr; wire [15:0] psra=iispsr[31:16]; //用于从系统时钟分频生成CODECLK wire [15:0] psrb=iispsr[15:0]; //用于从系统时钟分频生成sck wire sel_iispsr = (wb_addr_i==IISPSR_ADDR) & (|wb_sel_i) & (wb_cyc_stb); always @( posedge clk or negedge rstn) begin if(~rstn) iispsr<={16'd31,16'd3}; else if(sel_iispsr & wb_we_i & wb_ack_o) begin iispsr <=wb_data_i; end end //3. IISCON reg [3:0] iisfcon; wire txfifo_mode= iisfcon[3]; wire rxfifo_mode= iisfcon[2]; wire rxfifo_enable= iisfcon[1]; wire txfifo_enable= iisfcon[0]; wire [4:0] tx_fifo_cnt; wire [4:0] rx_fifo_cnt; wire [15:0] iisfcon_rd= {iisfcon,tx_fifo_cnt,rx_fifo_cnt} ; wire sel_iisfcon = (wb_addr_i==IISFCON_ADDR) & (|wb_sel_i) & (wb_cyc_stb); always @( posedge clk or negedge rstn) begin if(~rstn) iisfcon<=4'h3; else if(sel_iisfcon & wb_we_i & wb_ack_o) begin iisfcon <=wb_data_i[15:12]; end end //4. iis_rx_fifo和iis_tx_fifo; //wishbone read & write reg tx_rdreq,tx_wrreq; reg [31:0] tx_fifo_data; wire sel_iisfifo = (wb_addr_i==IISFIFO_ADDR) & (|wb_sel_i) & (wb_cyc_stb); //tx fifo write. always @( posedge clk or negedge rstn) begin if(~rstn) begin tx_wrreq<=1'b0; end else if(sel_iisfifo & wb_we_i & wb_ack_o) begin tx_wrreq<=1'b1; tx_fifo_data<= wb_data_i; end else begin tx_wrreq<=1'b0; end end //IISL3_ADDR reg [15:0] l3_reg_out; reg [5:0] l3_reg_in; reg l3_trig; reg l3_reg_in_rdy; wire sel_iisl3 = (wb_addr_i==IISL3_ADDR) & (|wb_sel_i) & (wb_cyc_stb); //write loigc of L3 register always @(posedge clk or negedge rstn) begin if(~rstn) begin l3_trig<=1'b0; l3_reg_out<=0; end else if(sel_iisl3 & wb_we_i & wb_ack_o) begin l3_trig<=1'b1; l3_reg_out<=wb_data_i[15:0]; end else l3_trig<=1'b0; end //WISHBONE总线读逻辑 wire selected=sel_iisl3|sel_iiscon|sel_iismod|sel_iisfcon|sel_iisfifo|sel_iispsr; wire wr_ack = wb_we_i & (sel_iisl3|sel_iiscon|sel_iismod|sel_iisfcon |sel_iispsr| (sel_iisfifo& (!tx_fifo_full))); reg rd_fifo_ack; wire rd_ack = (~wb_we_i) &( (sel_iiscon|sel_iismod|sel_iisfcon|sel_iispsr|sel_iisl3)| rd_fifo_ack); assign wb_ack_o= wr_ack | rd_ack; assign wb_rty_o=1'b0; assign wb_data_o =sel_iiscon? {32'b0,iiscon_rd}:sel_iismod?{32'b0,iismod}: sel_iispsr?{32'b0,iispsr}:sel_iisfcon?{32'b0,iisfcon_rd}: sel_iisl3?{32'b0,l3_reg_in_rdy,l3_reg_in}: {16'b0,rx_fifo_data}; wire rx_fifo_rdreq = sel_iisfifo & !wb_we_i & (|wb_sel_i); always @(posedge clk or negedge rstn) begin if(~rstn) begin rd_fifo_ack<=1'b0; end else begin rd_fifo_ack<=rx_fifo_rdreq?1'b1:1'b0; end end assign wb_err_o= 0 //发生FIFO逻辑 wire fifo_aclr=~rstn; reg rx_wrreq; wire tx_fifo_sclr=~txfifo_enable; wire rx_fifo_sclr=~rxfifo_enable; wire [31:0] tx_fifo_q; wire [31:0] rx_fifo_q; fifo32x32 tx_fifo(.data(tx_fifo_data),.wrreq(tx_wrreq),.rdreq(tx_rdreq),.clock(clk) , .aclr(fifo_aclr), .sclr(tx_fifo_sclr),.q(tx_fifo_q),.full(tx_fifo_full),.empty(tx_fifo_empty),.usedw(t x_fifo_cnt)); fifo32x32 rx_fifo(.data(rx_fifo_data),.wrreq(rx_wrreq),.rdreq(rx_fifo_rdreq), .clock(clk), .aclr(fifo_aclr),.sclr(rx_fifo_sclr),.q(rx_fifo_q),.full(rx_fifo_full),.empty(rx_fif o_empty),.usedw(rx_fifo_cnt) ); always @(posedge clk or negedge rstn) begin if(~rstn) begin dma_req<=2'b0; end else begin if(dma_ack[1]) dma_req[1]<=1'b0; else if(rx_fifo_cnt>24) dma_req[1]<=1'b1; if(dma_ack[0]) dma_req[0]<=1'b0; else if(tx_fifo_cnt<8) dma_req[0]<=1'b1; end end //IIS接口逻辑,生成sck,ws,datai,cdclk。 //first is the cdclk and sck reg [10:0] psra_cnt; reg [6:0] psrb_cnt; wire [10:0] psra_cnt_mark=(psra)>>1; wire [6:0] psrb_cnt_mark=(psrb)>>1; //reg sck,cdclk; assign sck = psra_cnt<=psra_cnt_mark; assign cdclk = psrb_cnt<=psrb_cnt_mark; always @(posedge clk or negedge rstn) begin if(~rstn) begin psra_cnt<=0; psrb_cnt<=0; end else if(tx_chl_idle_cmd&tx_chl_idle_cmd) begin psra_cnt<=0; psrb_cnt<=0; end else if(iis_ena) begin if(psra_cnt<(psra)) begin psra_cnt<=psra_cnt+1; end else begin psra_cnt<=0; end if(psrb_cnt<(psrb)) begin psrb_cnt<=psrb_cnt+1; end else begin psrb_cnt<=0; end end end //生成ws reg sck_q; always @ (posedge clk) begin sck_q<=sck;end wire posedge_sck=sck &(!sck_q); wire negedge_sck=(!sck) &(sck_q); reg datao; reg [4:0] ws_cnt; reg [31:0] serial_out; reg [31:0] serial_in; assign ws= active_level_left?(ws_cnt>4'd15):(!(ws_cnt>4'd15)); always @(posedge clk or negedge rstn) begin if(~rstn) begin ws_cnt<=5'h1F;tx_rdreq<=1'b0;serial_out<=0;datao<=0; rx_wrreq<=1'b0; serial_in<=0; end else begin if(iis_ena) begin if(negedge_sck) begin ws_cnt<=ws_cnt-1; end if(posedge_sck && ws_cnt==5'h1F && !tx_chl_idle_cmd && !tx_fifo_empty) begin tx_rdreq<=1'b1; end else tx_rdreq<=1'b0; if(tx_rdreq) serial_out<=tx_fifo_q;//data ready if(negedge_sck) datao<=serial_out[ws_cnt]; //rx logic. if(negedge_sck && ws_cnt==5'h1E && !rx_chl_idle_cmd && !rx_fifo_full) begin rx_wrreq<=1'b1; rx_fifo_data<=serial_in; end else rx_wrreq<=1'b0; if(posedge_sck) begin if(ws_cnt<5'h1F) serial_in[ws_cnt+1]<=datai; else serial_in[0]<=datai; end end end //L3接口,不是IIS的一部分,uda1341ts需要L3接口进行初始化 reg l3data_ena; wire l3datai=l3data; reg l3datao; assign l3data=l3data_ena?l3datao : 1'bz; parameter L3_READ=1'b0; parameter L3_WRITE=1'b1; reg l3_wr_or_rd; end reg [4:0] l3_state; parameter L3_IDLE=5'd1, L3_ADDR=5'd2, L3_SHIFT_OUT=5'd4, L3_SHIFT_IN=5'd8, L3_ADDR_END=5'd16; reg [11:0] l3_cnt_low; reg [3:0] l3_cnt_high; reg l3mode,l3clock; wire [2:0] l3_shiftin_index=l3_cnt_high[3:1]-1; wire [7:0] l3_data_out_high_byte=l3_reg_out[15:8]; always @(posedge clk or negedge rstn) begin if(~rstn) begin l3_reg_in<=0; l3_wr_or_rd<=L3_WRITE; l3_state<=L3_IDLE; l3mode<=1'b1; l3clock<=1'b1; l3data_ena<=1'b0; l3_cnt_low<=0; l3_cnt_high<=0; l3_reg_in_rdy<=1'b0; l3datao<=1'b0; end else begin case (l3_state) L3_IDLE:begin if(l3_trig) begin l3_state<=L3_ADDR; l3mode<=1'b0; l3_cnt_low<=0; l3clock<=1'b1; l3data_ena<=1'b0; l3_cnt_high<=0; l3_cnt_low<=1; end else begin l3_cnt_low<=l3_cnt_low+1; if(l3_cnt_low==psra[11:0]) begin l3data_ena<=1'b0; end end end L3_ADDR: begin l3_reg_in_rdy<=1'b0; if(l3_cnt_low<psra[11:0]) begin l3_cnt_low<=l3_cnt_low+1; end else begin l3_cnt_low<=0; end if(l3_cnt_low==0) begin l3clock<=~l3clock; if(l3_cnt_high==4'hf) begin l3_cnt_high<=0; l3_state<=L3_ADDR_END; end else begin l3_cnt_high<=l3_cnt_high+1; l3data_ena<=1'b1; l3datao<=l3_reg_out[l3_cnt_high[3:1]]; end end end L3_ADDR_END:begin if(l3_cnt_low<(psra[11:0])) begin l3_cnt_low<=l3_cnt_low+1; end else begin l3_cnt_low<=1; l3mode<=1'b1; if(l3_reg_out[0]==1'b1) begin l3_state<=L3_SHIFT_IN; l3data_ena<=1'b0; end else begin l3_state<=L3_SHIFT_OUT; end end end L3_SHIFT_IN:begin if(l3_cnt_low<psra[11:0]) begin l3_cnt_low<=l3_cnt_low+1; end else begin l3_cnt_low<=0; end if(l3_cnt_low==0) begin if(l3_cnt_high==4'hf) begin l3_cnt_low<=1; l3_cnt_high<=0; l3_state<=L3_IDLE; l3_reg_in_rdy<=1'b1; end else begin l3_cnt_high<=l3_cnt_high+1; l3clock<=~l3clock;l3_reg_in[l3_shiftin_index]<=l3datai; end end end L3_SHIFT_OUT: begin//shift data out if(l3_cnt_low<psra[11:0]) begin l3_cnt_low<=l3_cnt_low+1; end else begin l3_cnt_low<=0; end if(l3_cnt_low==0) begin l3clock<=~l3clock; if(l3_cnt_high==4'hf) begin l3_cnt_high<=0; l3_state<=L3_IDLE;end else begin l3_cnt_high<=l3_cnt_high+1; l3data_ena<=1'b1; l3datao<=l3_data_out_high_byte[l3_cnt_high[3:1]]; end end end default:begin l3_state<=L3_IDLE; end endcase end end endmodule