Verilog IIC驱动| FPGA驱动

cpp 复制代码
module iic(
    input                               clk                        ,
    input                               rst                       ,
    input              [   4:0]         cmd                        ,
    input                               cmd_vld                    ,
    input              [   7:0]         wr_data                    ,
    output             [   7:0]         rd_data                    ,
    output                              rd_data_vld                ,

    output reg                          rev_ack                    ,
    output                              done                       ,
    output reg                          scl                        ,
    inout                               sda                         

);

/*
模拟过程
1. cnt_num
2. 二阶状态机
3. scl的生成

cmd和cmd_vld是一起过来的!!!
*/

`define     START_BIT   5'b00001
`define     WRITE_BIT   5'b00010
`define     READ_BIT    5'b00100
`define     STOP_BIT    5'b01000
`define     ACK_BIT     5'b10000
`define     ACK         0
`define     NO_ACK      1

localparam STATUS_IDLE      = 7'b000_0001;
localparam STATUS_START     = 7'b000_0010;
localparam STATUS_WR_DATA   = 7'b000_0100;
localparam STATUS_RD_DATA   = 7'b000_1000;
localparam STATUS_T_ACK     = 7'b001_0000;
localparam STATUS_R_ACK     = 7'b010_0000;
localparam STATUS_STOP      = 7'b100_0000;
localparam T = 100_000;
localparam SCL_MAX = 50_000_000/T; // 一个完整的SCL周期

/*
localparam和define放在最前面
*/


reg	[3:0]cnt_num	   	;
reg [6:0]cstate;
reg [6:0]nstate;
reg [7:0]wr_data_r;
reg sda_out;


parameter   SCL_LOW_HALF  = (SCL_MAX * 1 / 4) - 1,
            SCL_HIGH_HALF = (SCL_MAX * 3 / 4) - 1;  
reg [25:0]cnt_bit;
// 下面是关键的 状态转换的检测信号






reg OE;
reg[8*20-1:0]state_name;
reg [7:0]rd_data_r;
reg [4:0]num;
wire end_cnt_num;

/*下面是输入输出信号*/
assign rd_data = rd_data_r; //读也从寄存器开始输出

assign sda = OE?sda_out:1'bz;
wire sda_in = sda;


reg [4:0]cmd_r;


wire IF_IDLE_START       = (cstate == STATUS_IDLE       )    && cmd_vld      && (cmd & `START_BIT)    ;
wire IF_START_WR_DATA    = (cstate == STATUS_START      )    && end_cnt_num  && (cmd_r & `WRITE_BIT)    ;
wire IF_WR_DATA_R_ACK    = (cstate == STATUS_WR_DATA    )    && end_cnt_num                             ;
wire IF_R_ACK_IDLE       = (cstate == STATUS_R_ACK      )    && end_cnt_num  && !(cmd_r & `STOP_BIT)    ; // 可以接收到响应后停止(比如说在读数据之前)
wire IF_IDLE_WR_DATA     = (cstate == STATUS_IDLE       )    && cmd_vld      && (cmd & `WRITE_BIT)    ;
wire IF_R_ACK_STOP       = (cstate == STATUS_R_ACK      )    && end_cnt_num  && (cmd_r & `STOP_BIT)     ;
wire IF_STOP_IDLE        = (cstate == STATUS_STOP       )    && end_cnt_num                             ;
wire IF_IDLE_RD_DATA     = (cstate == STATUS_IDLE       )    && cmd_vld      && (cmd & `READ_BIT)     ;
wire IF_RD_DATA_T_ACK    = (cstate == STATUS_RD_DATA    )    && end_cnt_num                             ;
wire IF_T_ACK_IDLE       = (cstate == STATUS_T_ACK      )    && end_cnt_num  && !(cmd_r & `STOP_BIT)    ;
wire IF_T_ACK_STOP       = (cstate == STATUS_T_ACK      )    && end_cnt_num  && (cmd_r & `STOP_BIT)     ;

/*
cmd_vld对应cmd   				是由外界的cmd触发的
end_cnt_num对应cmd_r			是由自动触发引起的
*/


assign add_cnt_bit = cstate != STATUS_IDLE;
assign end_cnt_bit = add_cnt_bit && cnt_bit == SCL_MAX - 1'd1;

assign add_cnt_num = end_cnt_bit;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;

assign done = IF_R_ACK_IDLE || IF_STOP_IDLE || IF_T_ACK_IDLE;//都是回到IDLE  用来对应eeprom的cnt_byte
assign rd_data_vld = IF_T_ACK_STOP || IF_T_ACK_IDLE;// 快发送ACK的时候

/*
    cnt_bit的目的是组成完整的scl时钟周期
*/ 
always @(posedge clk or negedge rst)
	if (!rst) begin
        cnt_bit <= 26'd0;
    end else if(add_cnt_bit)begin
        if(end_cnt_bit)
            cnt_bit <= 26'd0;
        else
            cnt_bit <= cnt_bit + 26'd1;
    end 

//IIC_SCL
always @(posedge clk or negedge rst) begin
	if (!rst) begin
		scl <= 1'b1;
    end else if(cnt_bit == (SCL_MAX - 1'd1)>>1 || IF_STOP_IDLE)begin
        scl <= 1'b1;
    end else if(end_cnt_bit)begin
        scl <= 1'b0;//start信号  默认是1 过了第一个end_cnt_bit才把scl拉低  妙啊
    end
end


// 能用寄存器   都用寄存器


always @(posedge clk or negedge rst) 
	if (!rst) begin
        OE = 1'b1;
    end else if(IF_IDLE_START || IF_START_WR_DATA || IF_IDLE_WR_DATA || IF_RD_DATA_T_ACK || IF_T_ACK_STOP || IF_R_ACK_STOP)begin// !!!添加了IF_T_ACK_STOP
        OE = 1'b1;
    end else if(IF_WR_DATA_R_ACK || IF_IDLE_RD_DATA || IF_STOP_IDLE)begin
        OE = 1'b0;
    end

 always @(posedge clk or negedge rst) begin
	if (!rst) begin
		sda_out <= 1;
		end
	else begin
		case (cstate)
			STATUS_IDLE    :sda_out <= 1;
			STATUS_START   :begin
                if(cnt_bit == SCL_LOW_HALF)
                    sda_out <= 1;
                else if(cnt_bit == SCL_HIGH_HALF)
                    sda_out <= 0;
				end
			STATUS_WR_DATA :begin
                if(cnt_bit == SCL_LOW_HALF)
                    sda_out <= wr_data_r[7-cnt_num];
				end
			STATUS_T_ACK   :begin//
                if(cnt_bit == SCL_LOW_HALF)
                    if(cmd_r & `ACK_BIT)
                        sda_out <= `NO_ACK;
                    else
                        sda_out <= `ACK;
				end
			STATUS_STOP    :begin
                if(cnt_bit == SCL_LOW_HALF)
                    sda_out <= 0;
                else if(cnt_bit == SCL_HIGH_HALF)
                    sda_out <= 1;
				end
			default: sda_out <= 'b1;
		endcase
	end
end


always@(*)
    case(cstate)
        STATUS_IDLE    :   state_name  = "IDLE   ";
        STATUS_START   :   state_name  = "START  ";
        STATUS_WR_DATA :   state_name  = "WR_DATA";
        STATUS_RD_DATA :   state_name  = "RD_DATA";
        STATUS_T_ACK   :   state_name  = "T_ACK  ";
        STATUS_R_ACK   :   state_name  = "R_ACK  ";
        STATUS_STOP    :   state_name  = "STOP   ";
        default 	   :   state_name  = "IDLE   ";
    endcase



always @(posedge clk or negedge rst) begin
	if (!rst) begin
		rev_ack <= 0;
		rd_data_r <= 8'b0;
	end else case (cstate)
        STATUS_RD_DATA:
            if(cnt_bit == SCL_HIGH_HALF)
	            rd_data_r[7 - cnt_num] <= sda;
        STATUS_R_ACK:
            if(cnt_bit == SCL_HIGH_HALF)
                rev_ack <= sda_in;    
        default:;
    endcase
end

/*
在scl的1/4发送数据,在3/4处接受数据
*/













always @(*) begin
	case (cstate)
		STATUS_IDLE    : num = 1;
		STATUS_START   : num = 1;
		STATUS_WR_DATA : num = 8;
		STATUS_RD_DATA : num = 8;
		STATUS_R_ACK   : num = 1;
		STATUS_T_ACK   : num = 1;
		STATUS_STOP    : num = 1;
		default : num = 1;
	endcase
end



// 把输入的东西先存放在寄存器里面  确保在状态机运行期间,这些信号的值保持稳定
always @(posedge clk or negedge rst) begin
	if (!rst) begin
		wr_data_r <= 'd0;
		cmd_r <= 'd0;
		end
	else if (cmd_vld) begin
		wr_data_r <= wr_data;
		cmd_r <= cmd;
		end
end




always@(posedge clk or negedge rst)
    if(!rst)begin
        cstate <= STATUS_IDLE;
    end else begin
        cstate <= nstate;
    end

always @(*) begin
	case(cstate)
		STATUS_IDLE    : begin
			if (IF_IDLE_START) begin
				nstate = STATUS_START;
				end
			else if (IF_IDLE_WR_DATA) begin
				nstate = STATUS_WR_DATA;
				end
			else if (IF_IDLE_RD_DATA) begin
				nstate = STATUS_RD_DATA;
				end
			else begin
				nstate = cstate;    //这个要注意
				end
			end 
		STATUS_START   : begin
			if (IF_START_WR_DATA) begin
				nstate = STATUS_WR_DATA;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_WR_DATA : begin
			if (IF_WR_DATA_R_ACK) begin
				nstate = STATUS_R_ACK;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_RD_DATA : begin
			if (IF_RD_DATA_T_ACK) begin
				nstate = STATUS_T_ACK;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_R_ACK   : begin
			if (IF_R_ACK_STOP) begin
				nstate = STATUS_STOP;
				end
			else if (IF_R_ACK_IDLE) begin
				nstate = STATUS_IDLE;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_T_ACK   : begin
			if (IF_T_ACK_STOP) begin
				nstate = STATUS_STOP;
				end
			else if (IF_T_ACK_IDLE) begin
				nstate = STATUS_IDLE;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_STOP    : begin
			if (IF_STOP_IDLE) begin
				nstate = STATUS_IDLE;
				end
			else begin
				nstate = cstate;
				end
			end 
		default : nstate = cstate;
	endcase
end




always @(posedge clk or negedge rst)begin 
	if(!rst)begin
		cnt_num <= 'd0;
		end 
	else if(add_cnt_num)begin 
		if(end_cnt_num)begin 
			cnt_num <= 'd0;
			end
		else begin 
			cnt_num <= cnt_num + 1'd1;
		end 
	end
end 










endmodule
相关推荐
soulermax3 小时前
华为数字芯片机考2025合集1已校正
华为·fpga开发·架构·系统架构·硬件架构
一条九漏鱼4 小时前
verilog有符号数的乘法
fpga开发
soulermax4 小时前
华为数字芯片机考2025合集3已校正
嵌入式硬件·华为·fpga开发
桐生大古16 小时前
verilog状态机思想编程流水灯
fpga开发
Apple6666666666616 小时前
状态机思想编程
fpga开发
Wnq1007218 小时前
论伺服电机在轨道式巡检机器人中的优势及应用实践
机器人·驱动·伺服电机·巡检机器人·北京玉麟科技·轨道式巡检机器人·防爆巡检机器人
做一个优雅的美男子18 小时前
特权FPGA之数码管
fpga开发
浮梦终焉19 小时前
VS Code下开发FPGA——FPGA开发体验提升__下
ide·fpga开发·verilog·vs code
Dlrbw1 天前
FPGA——状态机实现流水灯
fpga开发
博览鸿蒙1 天前
FPGA设计职位介绍|如何成为一名合格的数字前端设计工程师?
前端·fpga开发