IIC协议的Verilog代码(1)——主机写模块开发

Verilog代码

//Module Name:IIC Write
//Author:Yang Cheng Yu
//Date:2020/4/21
//==================defines=====================
`define SIM
module	iic_write(
	//================System Signal================
	input				clk				,
	input				rst_n			,
	//================Interface====================
	output	reg 		scl				,
	inout 				sda				,
	output reg 			flag_wr			,
	input				wr_req			,
	output	reg 		flag_wr_done	,
	input[6:0]			dev_addr		,
	input[7:0]			data_wr			,
	output reg[5:0]		state			
);
	//================parameters===================
	parameter			SYS_FRE				=50_000_000;
	parameter			IIC_FRE				=10_000;
	`ifndef SIM
	localparam			CNT_BAUD			=SYS_FRE/IIC_FRE;
	localparam			CNT_BAUD_HALF		=CNT_BAUD/2;
	`else
	localparam			CNT_BAUD 			= 500;
	localparam			CNT_BAUD_HALF 		= 250;
	`endif
	//state machine
	localparam			S_IDLE				=6'b0000_01;
	localparam			S_START				=6'b0000_10;
	localparam			S_DEV_ADDR			=6'b0001_00;
	localparam			S_ACK_DEV_ADDR		=6'b0010_00;
	localparam			S_DATA				=6'b0100_00;
	localparam			S_ACK_DATA			=6'b1000_00;
	//================System regs==================
	reg[15:0]			cnt_baud;
	reg[2:0]			cnt_bit;
	wire				flag_bit;
	reg 				flag_start_bit;
	reg 				SDA;
	reg 				wr_req_t0;
	reg 				wr_req_t1;
	wire 				wr_req_trig;
	reg 				flag_get_ack;
	reg[6:0]			dev_addr_reg;
	reg[7:0]			data_wr_reg;
	//================Main Codes===================
	//scl
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			scl <= 1'b1;
		else if(flag_start_bit==1'b1&&flag_wr==1'b1)
			scl <= 1'b1;
		else if(flag_start_bit==1'b0&&flag_wr==1'b1)
			if(cnt_baud<=CNT_BAUD_HALF-1)
				scl <= 1'b0;
			else if(cnt_baud>CNT_BAUD_HALF-1)
				scl <= 1'b1;
		else
			scl <= 1'b1;
	end
	
	//flag_wr
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			flag_wr <= 1'b0;
		else if(flag_wr==1'b0&&wr_req_trig==1'b1)
			flag_wr <= 1'b1;
		else if(state==S_ACK_DATA&&flag_bit==1'b1)
			flag_wr <= 1'b0;
	end
	
	//sda	
	assign sda = (state==S_ACK_DATA||state==S_ACK_DEV_ADDR)?1'bz:SDA;
	//cnt_baud
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			cnt_baud <= 'd0;
		else if(flag_start_bit==1'b1&&flag_wr==1'b1)
			if(cnt_baud==CNT_BAUD_HALF-1)
				cnt_baud <= 'd0;
			else
				cnt_baud <= cnt_baud + 1'b1;
		else if(flag_start_bit==1'b0&&flag_wr==1'b1)
			if(cnt_baud==CNT_BAUD-1)
				cnt_baud <= 'd0;
			else
				cnt_baud <= cnt_baud + 1'b1;
		else
			cnt_baud <= 'd0;
	end
	
	//cnt_bit
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			cnt_bit <= 'd0;
		else case(state)
			S_IDLE:cnt_bit <= 'd0;		
			S_START:cnt_bit <= 'd0;
			S_DEV_ADDR:begin
				if(cnt_bit==3'd7&&flag_bit==1'b1)
					cnt_bit <= 'd0;
				else if(flag_bit==1'b1)
					cnt_bit <= cnt_bit + 1'b1;
			end
			S_ACK_DEV_ADDR:cnt_bit <= 'd0;
			S_DATA:begin
				if(cnt_bit==3'd7&&flag_bit==1'b1)
					cnt_bit <= 'd0;
				else if(flag_bit==1'b1)
					cnt_bit <= cnt_bit + 1'b1;
			end
			S_ACK_DATA:cnt_bit <= 'd0;
		endcase
	end
	
	//flag_bit
	assign flag_bit = (cnt_baud==CNT_BAUD-1)?1'b1:1'b0;
	//flag_start_bit
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			flag_start_bit <= 1'b0;
		else if(flag_wr==1'b0&&wr_req_trig==1'b1)
			flag_start_bit <= 1'b1;
		else if(flag_start_bit==1'b1&&cnt_baud==CNT_BAUD_HALF-1)
			flag_start_bit <= 1'b0;
	end
	
	//state machine
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			state <= S_IDLE;
		else case(state)
			S_IDLE:begin
				if(wr_req_trig==1'b1)
					state <= S_START;
				else
					state <= S_IDLE;
			end
			S_START:begin
				if(cnt_baud==CNT_BAUD_HALF-1)
					state <= S_DEV_ADDR;
				else
					state <= S_START;
			end
			S_DEV_ADDR:begin
				if(cnt_bit==3'd7&&flag_bit==1'b1)
					state <= S_ACK_DEV_ADDR;
				else
					state <= S_DEV_ADDR;
			end
			S_ACK_DEV_ADDR:begin
				if(cnt_baud<CNT_BAUD-1&&flag_get_ack==1'b0)
					state <= S_ACK_DEV_ADDR;
				else if(cnt_baud==CNT_BAUD-1&&flag_get_ack==1'b1)
					state <= S_DATA;
				else if(cnt_baud==CNT_BAUD-1&&flag_get_ack==1'b0)
					state <= S_IDLE;
			end
			S_DATA:begin
				if(cnt_bit==3'd7&&flag_bit==1'b1)
					state <= S_ACK_DATA;
				else
					state <= S_DATA;
			end
			S_ACK_DATA:begin
				if(cnt_baud==CNT_BAUD-1)
					state <= S_IDLE;
				else
					state <= S_ACK_DATA;
			end
			default:state <= S_IDLE;
		endcase
	end
	
	//SDA
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			SDA <= 1'b1;
		else case(state)
			S_IDLE:SDA <= 1'b1;
			S_START:SDA <= 1'b0;
			S_DEV_ADDR:begin
				if(cnt_baud==CNT_BAUD_HALF/2-1)
					case(cnt_bit)
						0:SDA <= dev_addr_reg[6];
						1:SDA <= dev_addr_reg[5];
						2:SDA <= dev_addr_reg[4];
						3:SDA <= dev_addr_reg[3];
						4:SDA <= dev_addr_reg[2];
						5:SDA <= dev_addr_reg[1];
						6:SDA <= dev_addr_reg[0];
						7:SDA <= 1'b1;
						default:SDA <= 1'b1;
					endcase
			end
			S_ACK_DEV_ADDR:SDA <= 1'b1;
			S_DATA:begin
				if(cnt_baud==CNT_BAUD_HALF/2-1)
					case(cnt_bit)
						0:SDA <= data_wr_reg[7];
						1:SDA <= data_wr_reg[6];
						2:SDA <= data_wr_reg[5];
						3:SDA <= data_wr_reg[4];
						4:SDA <= data_wr_reg[3];
						5:SDA <= data_wr_reg[2];
						6:SDA <= data_wr_reg[1];
						7:SDA <= data_wr_reg[0];
						default:SDA <= 1'b1;
					endcase
			end
			S_ACK_DATA:SDA <= 1'b1;
			default:SDA <= 1'b1;
		endcase
	end
	
	//wr_req and wr_req_trig
	always	@(posedge clk)begin
		wr_req_t0 <= wr_req;
		wr_req_t1 <= wr_req_t0;
	end
	assign	wr_req_trig = wr_req_t0&(~wr_req_t1);
	//flag_get_ack
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			flag_get_ack <= 1'b0;
		else case(state)
			S_ACK_DEV_ADDR:begin
				if(cnt_baud==CNT_BAUD_HALF+CNT_BAUD_HALF/2-2&&sda==1'b1)
					flag_get_ack <= 1'b1;
			end
			S_ACK_DATA:begin
				if(cnt_baud==CNT_BAUD_HALF+CNT_BAUD_HALF/2-2&&sda==1'b1)
					flag_get_ack <= 1'b1;				
			end
			default:flag_get_ack <= 1'b0;
		endcase
	end
	
	//flag_wr_done
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			flag_wr_done <= 1'b0;
		else if(state==S_ACK_DATA&&flag_get_ack==1'b1)
			flag_wr_done <= 1'b1;
		else
			flag_wr_done <= 1'b0;
	end
	
	//dev_addr_reg
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			dev_addr_reg <= 'd0;
		else if(flag_wr==1'b0&&wr_req_trig==1'b1)
			dev_addr_reg <= dev_addr;
	end
	
	//data_wr_reg
	always	@(posedge clk or negedge rst_n)begin
		if(rst_n==1'b0)
			data_wr_reg <= 'd0;
		else if(flag_wr==1'b0&&wr_req_trig==1'b1)
			data_wr_reg <= data_wr;
	end
	
endmodule

Test Bench

`timescale	1ns/1ns		//时间精度
`define clock_period 20	//时钟周期
module tb_iic_write;	//实体名称
	//state machine
	localparam			S_IDLE				=6'b0000_01;
	localparam			S_START				=6'b0000_10;
	localparam			S_DEV_ADDR			=6'b0001_00;
	localparam			S_ACK_DEV_ADDR		=6'b0010_00;
	localparam			S_DATA				=6'b0100_00;
	localparam			S_ACK_DATA			=6'b1000_00;
//=====================<系统端口>=============================
	reg				clk				;
	reg				rst_n			;

//=====================<外设端口>=============================
	wire	 		scl				;
	wire 			sda				;
	wire  			flag_wr			;
	reg				wr_req			;
	wire	 		flag_wr_done	;
	reg[6:0]		dev_addr		;
	reg[7:0]		data_wr			;
	wire[5:0]		state			;
	assign	sda = (state==S_ACK_DATA||state==S_ACK_DEV_ADDR)?1'b1:1'bz;
	


iic_write iic_write_inst(
	//================System Signal================
	.clk				(clk),
	.rst_n				(rst_n),
	//================Interface====================
	.scl				(scl),
	.sda				(sda),
	.flag_wr			(flag_wr),
	.wr_req				(wr_req),
	.flag_wr_done		(flag_wr_done),
	.dev_addr			(dev_addr),
	.data_wr			(data_wr),
	.state				(state)
);
//=====================<时钟信号>=============================
initial begin
	clk = 1;
	forever
		#(`clock_period/2)	clk = ~clk;
end

//=====================<复位信号>=============================
initial begin
	rst_n = 0;#(`clock_period*20+1);
	rst_n = 1;
end

//=====================<激励信号>=============================
initial begin
	dev_addr = 7'b011_0101;
	data_wr  = 8'h00;
	wr_req	 = 0;
	#(`clock_period*20+1);//初始化
	data_wr = 8'h56;
	#20;
	wr_req = 1;
	#20;
	wr_req = 0;
	#20_0000;
	data_wr = 8'h78;
	dev_addr = 7'b100_1010;
	#20;
	wr_req = 1;
	#20;
	wr_req = 0;
end	

endmodule

波形图

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的I2C主机控制器的Verilog代码,用于在FPGA上实现I2C协议: ```verilog module i2c_master( input clk, input rst, input sda_i, output sda_o, inout scl_io ); parameter IDLE = 2'b00; parameter START = 2'b01; parameter WRITE = 2'b10; parameter READ = 2'b11; reg [7:0] data_reg; reg [6:0] addr_reg; reg [1:0] state_reg; reg [2:0] bit_cnt_reg; reg [2:0] byte_cnt_reg; reg [2:0] ack_cnt_reg; reg [2:0] stop_cnt_reg; reg [2:0] start_cnt_reg; assign sda_o = (state_reg == WRITE) ? data_reg[bit_cnt_reg] : 1'bz; always @(posedge clk) begin if (rst) begin state_reg <= IDLE; bit_cnt_reg <= 0; byte_cnt_reg <= 0; ack_cnt_reg <= 0; stop_cnt_reg <= 0; start_cnt_reg <= 0; end else begin case (state_reg) IDLE: begin if (!sda_i && !scl_io) begin state_reg <= START; start_cnt_reg <= 1; end end START: begin if (start_cnt_reg == 1) begin sda_o <= 0; start_cnt_reg <= 2; end else if (start_cnt_reg == 2) begin scl_io <= 0; start_cnt_reg <= 3; end else if (start_cnt_reg == 3) begin sda_o <= 1; start_cnt_reg <= 4; end else if (start_cnt_reg == 4) begin scl_io <= 1; state_reg <= WRITE; bit_cnt_reg <= 0; byte_cnt_reg <= 0; ack_cnt_reg <= 0; end end WRITE: begin if (ack_cnt_reg == 0) begin if (bit_cnt_reg == 7) begin ack_cnt_reg <= 1; bit_cnt_reg <= 0; byte_cnt_reg <= byte_cnt_reg + 1; if (byte_cnt_reg == 2) begin state_reg <= STOP; stop_cnt_reg <= 1; end else begin state_reg <= WRITE; end end else begin bit_cnt_reg <= bit_cnt_reg + 1; end end else if (ack_cnt_reg == 1) begin if (sda_i) begin state_reg <= IDLE; end else begin ack_cnt_reg <= 2; end end else if (ack_cnt_reg == 2) begin ack_cnt_reg <= 0; state_reg <= WRITE; end end READ: begin // TODO: implement read state end STOP: begin if (stop_cnt_reg == 1) begin sda_o <= 0; stop_cnt_reg <= 2; end else if (stop_cnt_reg == 2) begin scl_io <= 0; stop_cnt_reg <= 3; end else if (stop_cnt_reg == 3) begin sda_o <= 1; stop_cnt_reg <= 4; end else if (stop_cnt_reg == 4) begin scl_io <= 1; state_reg <= IDLE; end end endcase end end endmodule ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值