片上系统设计思想与源代码分析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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