zoukankan      html  css  js  c++  java
  • Verilog-I2S协议 (地平线2019)

    协议介绍

    代码

    master

    `timescale 1ns / 1ps
    
    module I2S_master(
    	input clk_in,
    	input [15:0] data_in,
    	input rstn,
    	input enable,
    	output DATA,
    	output WS,
    	output clk,
    	output send_over
        );
    	 
    assign clk = clk_in;
    
    localparam IDLE=2'b00;
    localparam LEFT=2'b01;
    localparam RIGHT=2'b11;
    reg [4:0] cnt;
    reg [1:0] state;
    reg [1:0] next_state;
    
    reg [17:0] data_send;
    
    always @(posedge clk or negedge rstn) begin
    	if(!rstn) state <= IDLE;
    	else if(enable) state <= next_state;
    	else state <= IDLE;
    end
    
    // 状态
    always @(*) begin
    	case(state) 
    		IDLE: next_state = LEFT;
    		LEFT: 
    			begin
    				if(cnt == 'd17) next_state = RIGHT;
    				else next_state = LEFT;
    			end
    		RIGHT:
    			begin
    				if(cnt == 'd17) next_state = LEFT;
    				else next_state = RIGHT;
    			end
    		default: next_state = IDLE;
    	endcase
    end
    
    // 发送比特计数
    always @(posedge clk or negedge rstn) begin
    	if(!rstn) cnt <= 'd0;
    	else if((state != IDLE) && (cnt != 'd17)) cnt <= cnt + 1'b1;
    	else cnt <= 'd0;
    end
    
    
    always @(posedge clk or negedge rstn) begin
    	if(!rstn) data_send <= 'd0;
    	else if((state == IDLE) || (cnt == 'd17)) data_send <= {data_in,2'b00}; // 装载数据
    	else data_send <= {data_send[16:0],1'b0};  // 在发送状态下对data_send进行左移位
    end
    
    assign WS = (state == LEFT)? 1'b1 : 1'b0;  
    assign DATA = data_send[17];  // 数据线为data_send寄存器的最高位
    assign send_over = (cnt == 'd15)? 1'b1 : 1'b0;
    
    endmodule
    
    

    slave

    `timescale 1ns / 1ps
    
    module I2S_slave(
    	input rstn,
    	input clk,
    	input WS,
    	input DATA,
    	
    	output reg[15:0] L_DATA,
    	output reg[15:0] R_DATA,
    	output recv_over
        );
    	 
    localparam IDLE = 2'b00;
    localparam GET_LEFT = 2'b01;
    localparam GET_RIGHT = 2'b11;
    
    reg [4:0] cnt;
    reg [1:0] state,next_state;
    
    reg WS_reg;
    wire WS_en;
    always @(negedge clk or negedge rstn) begin
    	if(!rstn) WS_reg <= 1'b0;
    	else WS_reg <= WS;
    end
    assign WS_en = (~WS_reg)&WS;  // 通过判断WS上升沿开始接收数据
    
    always @(negedge clk or negedge rstn) begin
    	if(!rstn) state <= IDLE;
    	else state <= next_state;
    end
    
    // cnt计数决定状态转移
    always @(*) begin
    	case(state)
    		IDLE:
    			begin
    				if(WS_en) next_state = GET_LEFT;
    				else next_state = IDLE;
    			end
    		GET_LEFT:
    			begin
    				if(cnt == 'd17) next_state = GET_RIGHT;
    				else next_state = GET_LEFT;
    			end
    		GET_RIGHT:
    			begin
    				if(cnt == 'd17) next_state = IDLE;
    				else next_state = GET_RIGHT;
    			end
    		default: next_state = IDLE;
    	endcase
    end
    
    always @(negedge clk or negedge rstn) begin
    	if(!rstn) cnt <= 'd0;
    	else if(WS_en || (state!=IDLE)) begin  // 注意这里从WS_en开始计数
    		if(cnt != 'd17)cnt <= cnt + 1'b1;
    		else cnt <= 'd0;
    	end
    	else cnt <= 'd0;
    end
    
    // 接收左声道数据
    always @(negedge clk or negedge rstn) begin
    	if(!rstn) L_DATA <= 'd0;
    	else if(WS && (cnt < 'd16)) 
    		L_DATA <= {L_DATA[14:0],DATA};  // 左移位
    	else L_DATA <= L_DATA;
    end
    
    // 接收右声道数据
    always @(negedge clk or negedge rstn) begin
    	if(!rstn) R_DATA <= 'd0;
    	else if(~WS && (cnt < 'd16)) 
    		R_DATA <= {R_DATA[14:0],DATA};  // 左移位
    	else R_DATA <= R_DATA;
    end
    
    assign recv_over = (cnt == 'd15)? 1'b1 : 1'b0;
    endmodule
    
    

    testbench

    `timescale 1ns / 1ps
    
    module I2S_master_tb;
    
    	// Inputs
    	reg clk_in;
    	reg [15:0] data_in;
    	reg rstn;
    	reg enable;
    
    	// Outputs
    	wire DATA;
    	wire WS;
    	wire clk;
    	wire send_over;
    	
    	wire [15:0] L_DATA;
       wire [15:0] R_DATA;
    	wire recv_over;
    
    	// Instantiate the Unit Under Test (UUT)
    	I2S_master u_I2S_master (
    		.clk_in(clk_in), 
    		.data_in(data_in), 
    		.rstn(rstn), 
    		.enable(enable), 
    		.DATA(DATA), 
    		.WS(WS), 
    		.clk(clk), 
    		.send_over(send_over)
    	);
    	
    	I2S_slave u_I2S_slave(
    		.rstn(rstn),
    	   .clk(clk),
    		.WS(WS),
    		.DATA(DATA),
    	
    		.L_DATA(L_DATA),
    		.R_DATA(R_DATA),
    		.recv_over(recv_over)
        );
    
    	initial begin
    		// Initialize Inputs
    		clk_in = 0;
    		data_in = 0;
    		rstn = 0;
    		enable = 0;
    		
    
    		// Wait 100 ns for global reset to finish
    		#100;
    		@(negedge clk);
    		rstn = 1;
    		enable = 1;
    		data_in = 16'b1010_0101_1010_0101;
    		
    		@(negedge send_over);
    		data_in = 16'b0101_1010_0101_1010;
            
    		// Add stimulus here
    
    	end
    	
    	always #20 clk_in = ~clk_in;
          
    endmodule
    
    

    仿真波形

    总结

    比较一下之前写的SPI协议,两者接收和发送数据方面有所不同,SPI是直接将计数值作为寄存器索引:

    // 接收数据
    always @(posedge clk or negedge rstn) begin
    	if(!rstn) rx_data <= 8'd0;
    	else begin
    		if(spi_state==TRANS && (~sclk)) rx_data[spi_count] <= miso;
    		else rx_data <= rx_data;
    	end
    end
    
    // 发送数据
    assign mosi = (spi_state == TRANS)? tx_data[spi_count] : 1'bz;  // 设为z态方便调试
    

    而上面的I2S是采用计数加移位的方式:

    //发送
    always @(posedge clk or negedge rstn) begin
    	if(!rstn) data_send <= 'd0;
    	else if((state == IDLE) || (cnt == 'd17)) data_send <= {data_in,2'b00}; // 装载数据
    	else data_send <= {data_send[16:0],1'b0};  // 在发送状态下对data_send进行左移位
    end
    
    // 接收左声道数据
    always @(negedge clk or negedge rstn) begin
    	if(!rstn) L_DATA <= 'd0;
    	else if(WS && (cnt < 'd16)) 
    		L_DATA <= {L_DATA[14:0],DATA};  // 左移位
    	else L_DATA <= L_DATA;
    end
    

    从综合的角度看,第二种实现方式更具有硬件思维

  • 相关阅读:
    div水平居中和垂直居中
    HTML流动布局各种宽度自适应
    PHP导出大量数据到excel表格
    等比例压缩图片到指定的KB大小
    SQLServer数据库中创建临时表
    Mysql数据库表关于几个int类型的字符长度
    JS 获取浏览器和屏幕宽高信息
    CTSC2018 & APIO2018 颓废 + 打铁记
    [UOJ#192]【UR #14】最强跳蚤
    ZJOI2018 Day2 滚粗记 + 流水账
  • 原文地址:https://www.cnblogs.com/wt-seu/p/12797593.html
Copyright © 2011-2022 走看看