Bootstrap

Verilog数字系统设计教程[第4版]夏宇闻——第16章EEPROM代码

Verilog数字系统设计教程[第4版]夏宇闻——第16章EEPROM代码

//-------------eeprom.v文件开始---------------------------
`timescale 1ns/1ns
`define timeslice 100

module EEPROM(scl,sda);
input scl;							//串行时钟线
inout sda;							//串行数据线
reg out_flag;						//SDA数据输出的控制信号
reg[7:0]		memory[2047:0];
reg[10:0] 	address;
reg[7:0]		memory_buf;
reg[7:0]		sda_buf;				//SDA数据输出寄存器
reg[7:0]		shift;				//SDA数据输入寄存器
reg[7:0]		addr_byte;			//EEPROM存储单元地址寄存器
reg[7:0]		ctrl_byte;			//控制字寄存器
reg[1:0]		State;				//状态寄存器
integer i;

//--------------------------------------------------------
parameter	r7=8'b10101111,	w7=8'b10101110,		//main7
				r6=8'b10101101,	w6=8'b10101100,		//main6
				r5=8'b10101011,	w5=8'b10101010,		//main5
				r4=8'b10101001,	w4=8'b10101000,		//main4
				r3=8'b10100111,	w3=8'b10100110,		//main3
				r2=8'b10100101,	w2=8'b10100100,		//main2
				r1=8'b10100011,	w1=8'b10100010,		//main1
				r0=8'b10100001,	w0=8'b10100000;		//main0
				
//--------------------------------------------------------		
assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz;

//-------------寄存器和存储器初始化---------------------------
initial
	begin
		addr_byte		=0;
		ctrl_byte		=0;
		out_flag			=0;
		sda_buf			=0;
		State				=2'b00;
		memory_buf		=0;
		address			=0;
		shift				=0;
		for(i=0;i<=2047;i=i+1)
			memory[i]=0;
	end

//--------------启动信号------------------------------------
always@(negedge sda)
	if(scl == 1)
		begin
			State = State + 1;	//注意:ModelSim6.1以上版本,认为从高阻态到1是负跳变沿
			if(State == 2'b11)
				disable write_to_eeprm;
		end
		
//--------------主状态机------------------------------------
always@(posedge sda)
	if(scl == 1)
		stop_W_R;					//停止操作
	else
		begin
			casex(State)
				2'b10:
					begin
						read_in;
						if(ctrl_byte==w7||ctrl_byte==w6||ctrl_byte==w5||ctrl_byte==w4||ctrl_byte==w3||ctrl_byte==w2||ctrl_byte==w1||ctrl_byte==w0)
							begin
								State = 2'b10;
								write_to_eeprm;		//写操作
							end
						else
							State = 2'b00;
					end
				2'b11:
					read_from_eeprm;						//读操作
				default:
					State=2'b00;
			endcase
		end													//主状态机结束
		
//-------------操作停止------------------------------------
task stop_W_R;
	begin
		State = 2'b00;
		addr_byte = 0;
		ctrl_byte = 0;
		out_flag = 0;
		sda_buf = 0;
	end
endtask

//-------------读进控制字和存储单元地址-----------------------
task read_in;
	begin
		shift_in(ctrl_byte);
		shift_in(addr_byte);
	end
endtask

//-------------EEPROM的写操作------------------------------
task write_to_eeprm;
	begin
		shift_in(memory_buf);
		address = {ctrl_byte[3:1],addr_byte};
		memory[address] = memory_buf;
		$display("eeprm------memory[%0h]=%0h",address,memory[address]);
		State = 2'b00;										//回到0状态
	end
endtask

//-------------EEPROM的读操作------------------------------
task read_from_eeprm;
	begin
		shift_in(ctrl_byte);
		if(ctrl_byte==r7||ctrl_byte==r6||ctrl_byte==r5||ctrl_byte==r4||ctrl_byte==r3||ctrl_byte==r2||ctrl_byte==r1||ctrl_byte==r0)
			begin
				address = {ctrl_byte[3:1],addr_byte};
				sda_buf = memory[address];
				shift_out;
				State = 2'b00;
			end
	end
endtask

//------SDA数据线上的数据存入寄存器,数据在SCL的高电平有效-------
task shift_in;
	output[7:0] shift;
	begin
		@(posedge scl) shift[7]=sda;
		@(posedge scl) shift[6]=sda;
		@(posedge scl) shift[5]=sda;
		@(posedge scl) shift[4]=sda;
		@(posedge scl) shift[3]=sda;
		@(posedge scl) shift[2]=sda;
		@(posedge scl) shift[1]=sda;
		@(posedge scl) shift[0]=sda;
		@(negedge scl)
			begin
				#`timeslice;
				out_flag = 1;							//应答信号输出
				sda_buf = 0;
			end
		@(negedge scl)
			#`timeslice out_flag = 0;
	end
endtask

//--EEPROM存储器中的数据通过SDA数据线输出,数据在SCL低电平时变化--
task shift_out;
	begin
		out_flag = 1;
		for(i=6;i>=0;i=i-1)
			begin
				@(negedge scl);
				#`timeslice;
				sda_buf = sda_buf<<1;
			end
		@(negedge scl) #`timeslice sda_buf[7] = 1;		//非应答信号输出
		@(negedge scl) #`timeslice out_flag = 0;
	end
endtask
endmodule

//-------------eeprom.v文件结束---------------------------
//----------eeprom_Wr.v文件开始----------------------------
`timescale 1ns/1ns
module EEPROM_WR(SDA,SCL,ACK,RESET,CLK,WR,RD,ADDR,DATA);
output SCL;							//串行时钟线
output ACK;							//读写一个周期的应答信号
input RESET;						//复位信号
input CLK;							//时钟信号输入
input WR,RD;						//读写信号
input [10:0] ADDR;				//地址线
inout SDA;							//串行数据线
inout [7:0] DATA;					//并行数据线
reg ACK;
reg SCL;
reg WF,RF;							//读写操作标志
reg FF;								//标志寄存器
reg [1:0] head_buf;				//启动信号寄存器
reg [1:0] stop_buf;				//停止信号寄存器
reg [7:0] sh8out_buf;			//EEPROM写寄存器
reg [8:0] sh8out_state;			//EEPROM写状态寄存器
reg [9:0] sh8in_state;			//EEPROM读状态寄存器
reg [2:0] head_state;			//启动状态寄存器
reg [2:0] stop_state;			//停止状态寄存器
reg [10:0] main_state;			//主状态寄存器
reg [7:0] data_from_rm;			//EEPROM读寄存器
reg link_sda;						//SDA数据输入EEPROM开关
reg link_read;						//EEPROM读操作开关
reg link_head;						//启动信号开关
reg link_write;					//EEPROM写操作开关
reg link_stop;						//停止信号开关
wire sda1,sda2,sda3,sda4; 

//----------串行数据在开关控制下有秩序的输出或输入----------
assign sda1 = (link_head)? head_buf[1] : 1'b0;
assign sda2 = (link_write)? sh8out_buf[7] : 1'b0;
assign sda3 = (link_stop)? stop_buf[1] : 1'b0;
assign sda4 = (sda1 | sda2 | sda3);
assign SDA = (link_sda)? sda4 : 1'bz;
assign DATA = (link_read)? data_from_rm : 8'hzz;

//----------主状态机状态定义----------------------------
parameter Idle				= 11'b00000000001,
			 Ready			= 11'b00000000010,
			 Write_start	= 11'b00000000100,
			 Ctrl_write		= 11'b00000001000,
			 Addr_write		= 11'b00000010000,
			 Data_write		= 11'b00000100000,
			 Read_start		= 11'b00001000000,
			 Ctrl_read		= 11'b00010000000,
			 Data_read		= 11'b00100000000,
			 Stop				= 11'b01000000000,
			 Ackn				= 11'b10000000000,
			 
//----------并行数据串行输出状态-------------------------
			 sh8out_bit7	= 9'b000000001,
			 sh8out_bit6	= 9'b000000010,
			 sh8out_bit5	= 9'b000000100,
			 sh8out_bit4	= 9'b000001000,
			 sh8out_bit3	= 9'b000010000,
			 sh8out_bit2	= 9'b000100000,
			 sh8out_bit1	= 9'b001000000,
			 sh8out_bit0	= 9'b010000000,
			 sh8out_end		= 9'b100000000;

//----------串行数据并行输出状态-------------------------
parameter sh8in_begin	= 10'b0000000001,
			 sh8in_bit7		= 10'b0000000010,
			 sh8in_bit6		= 10'b0000000100,
			 sh8in_bit5		= 10'b0000001000,
			 sh8in_bit4		= 10'b0000010000,
			 sh8in_bit3		= 10'b0000100000,
			 sh8in_bit2		= 10'b0001000000,
			 sh8in_bit1		= 10'b0010000000,
			 sh8in_bit0		= 10'b0100000000,
			 sh8in_end		= 10'b1000000000,
			 
//----------启动状态-----------------------------------
			 head_begin		= 3'b001,
			 head_bit		= 3'b010,
			 head_end		= 3'b100,
			 
//----------停止状态-----------------------------------
			 stop_begin		= 3'b001,
			 stop_bit		= 3'b010,
			 stop_end		= 3'b100;
			 
parameter YES				= 1,
			 NO				= 0;
			 
//----------产生串行时钟SCL,为输入时钟的2分频-------------
always@(negedge CLK)
	if(RESET)
		SCL<=0;
	else
		SCL<=~SCL;

//----------主状态机程序--------------------------------
always@(posedge CLK)
	if(RESET)
		begin
			link_read									<= NO;
			link_write									<= NO;
			link_head									<= NO;
			link_stop									<= NO;
			link_sda										<= NO;
			ACK											<= 0;
			RF												<= 0;
			WF												<= 0;
			FF												<= 0;
			main_state									<= Idle;
		end
	else
		begin
			casex(main_state)
				Idle:
					begin
						link_read						<= NO;
						link_write						<= NO;
						link_head						<= NO;
						link_stop						<= NO;
						link_sda							<= NO;
						if(WR)
							begin
								WF 						<= 1;
								main_state 				<= Ready;
							end
						else if(RD)
							begin
								RF 						<= 1;
								main_state 				<= Ready;
							end
						else
							begin
								WF 						<= 0;
								RF 						<= 0;
								main_state			   <= Idle;
							end
					end
				Ready:
					begin
						link_read						<= NO;
						link_write						<= NO;
						link_stop						<= NO;
						link_head						<= YES;
						link_sda							<= YES;
						head_buf[1:0]					<= 2'b10;
						stop_buf[1:0]					<= 2'b01;
						head_state						<= head_begin;
						FF									<= 0;
						ACK								<= 0;
						main_state						<= Write_start;
					end
				Write_start:
					if(FF==0)
						shift_head;
					else
						begin
							sh8out_buf[7:0]			<= {1'b1,1'b0,1'b1,1'b0,ADDR[10:8],1'b0};
							link_head					<= NO;
							link_write					<= YES;
							FF								<= 0;
							sh8out_state				<= sh8out_bit6;
							main_state					<= Ctrl_write;
						end
				Ctrl_write:
					if(FF==0)
						shift8_out;
					else
						begin
							sh8out_state				<= sh8out_bit7;
							sh8out_buf[7:0]			<= ADDR[7:0];
							FF								<= 0;
							main_state					<= Addr_write;
						end
				Addr_write:
					if(FF==0)
						shift8_out;
					else
						begin
							FF<=0;
							if(WF)
								begin
									sh8out_state		<= sh8out_bit7;
									sh8out_buf[7:0]	<= DATA;
									main_state			<= Data_write;
								end
							if(RF)
								begin
									head_buf				<= 2'b10;
									head_state			<= head_begin;
									main_state			<= Read_start;
								end
						end
				Data_write:
					if(FF==0)
						shift8_out;
					else
						begin
							stop_state					<= stop_begin;
							main_state					<= Stop;
							link_write					<= NO;
							FF								<= 0;
						end
				Read_start:
					if(FF==0)
						shift_head;
					else
						begin
							sh8out_buf					<= {1'b1,1'b0,1'b1,1'b0,ADDR[10:8],1'b1};
							link_head					<= NO;
							link_sda						<= YES;
							link_write					<= YES;
							FF								<= 0;
							sh8out_state				<= sh8out_bit6;
							main_state					<= Ctrl_read;
						end
				Ctrl_read:
					if(FF==0)
						shift8_out;
					else
						begin
							link_sda						<= NO;
							link_write					<= NO;
							FF								<= 0;
							sh8in_state					<= sh8in_begin;
							main_state					<= Data_read;
						end
				Data_read:
					if(FF==0)
						shift8in;
					else
						begin
							link_stop					<= YES;
							link_sda						<= YES;
							stop_state					<= stop_bit;
							FF								<= 0;
							main_state					<= Stop;
						end
				Stop:
					if(FF==0)
						shift_stop;
					else
						begin
							ACK							<= 1;
							FF								<= 0;
							main_state					<= Ackn;
						end
				Ackn:
					begin
						ACK								<= 0;
						WF									<= 0;
						RF									<= 0;
						main_state						<= Idle;
					end
				default: main_state					<= Idle;
			endcase
		end

//----------串行数据转换为并行数据任务-------------------------
task shift8in;
	begin
		casex(sh8in_state)
			sh8in_begin:
				sh8in_state								<= sh8in_bit7;
			sh8in_bit7:
				if(SCL)
					begin
						data_from_rm[7]				<= SDA;
						sh8in_state						<= sh8in_bit6;
					end
				else
					sh8in_state							<= sh8in_bit7;
			sh8in_bit6:
				if(SCL)
					begin
						data_from_rm[6]				<= SDA;
						sh8in_state						<= sh8in_bit5;
					end
				else
					sh8in_state							<= sh8in_bit6;
			sh8in_bit5:
				if(SCL)
					begin
						data_from_rm[5]				<= SDA;
						sh8in_state						<= sh8in_bit4;
					end
				else
					sh8in_state							<= sh8in_bit5;
			sh8in_bit4:
				if(SCL)
					begin
						data_from_rm[4]				<= SDA;
						sh8in_state						<= sh8in_bit3;
					end
				else
					sh8in_state							<= sh8in_bit4;
			sh8in_bit3:
				if(SCL)
					begin
						data_from_rm[3]				<= SDA;
						sh8in_state						<= sh8in_bit2;
					end
				else
					sh8in_state							<= sh8in_bit3;
			sh8in_bit2:
				if(SCL)
					begin
						data_from_rm[2]				<= SDA;
						sh8in_state						<= sh8in_bit1;
					end
				else
					sh8in_state							<= sh8in_bit2;
			sh8in_bit1:
				if(SCL)
					begin
						data_from_rm[1]				<= SDA;
						sh8in_state						<= sh8in_bit0;
					end
				else
					sh8in_state							<= sh8in_bit1;
			sh8in_bit0:
				if(SCL)
					begin
						data_from_rm[5]				<= SDA;
						sh8in_state						<= sh8in_end;
					end
				else
					sh8in_state							<= sh8in_bit0;
			sh8in_end:
				if(SCL)
					begin
						link_read						<= YES;
						FF									<= 1;
						sh8in_state						<= sh8in_bit7;
					end
				else
					sh8in_state							<= sh8in_end;
			default:
				begin
					link_read							<= NO;
					sh8in_state							<= sh8in_bit7;
				end
		endcase
	end
endtask

//----------并行数据转换为串行数据任务-------------------------
task shift8_out;
	begin
		casex(sh8out_state)
			sh8out_bit7:
				if(!SCL)
					begin
						link_sda							<= YES;
						link_write						<= YES;
						sh8out_state					<= sh8out_bit6;
					end
				else
					sh8out_state						<= sh8out_bit7;
			sh8out_bit6:
				if(!SCL)
					begin
						link_sda							<= YES;
						link_write						<= YES;
						sh8out_state					<= sh8out_bit5;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit6;
			sh8out_bit5:
				if(!SCL)
					begin
						sh8out_state					<= sh8out_bit4;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit5;
			sh8out_bit4:
				if(!SCL)
					begin
						sh8out_state					<= sh8out_bit3;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit4;
			sh8out_bit3:
				if(!SCL)
					begin
						sh8out_state					<= sh8out_bit2;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit3;
			sh8out_bit2:
				if(!SCL)
					begin
						sh8out_state					<= sh8out_bit1;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit2;
			sh8out_bit1:
				if(!SCL)
					begin
						sh8out_state					<= sh8out_bit0;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit1;
			sh8out_bit0:
				if(!SCL)
					begin
						sh8out_state					<= sh8out_end;
						sh8out_buf						<= sh8out_buf<<1;
					end
				else
					sh8out_state						<= sh8out_bit0;
			sh8out_end:
				if(!SCL)
					begin
						link_sda							<= NO;
						link_write						<= NO;
						FF									<= 1;
					end
				else
					sh8out_state						<= sh8out_end;
		endcase
	end
endtask

//----------输出启动信号任务-----------------------------------
task shift_head;
	begin
		casex(head_state)
			head_begin:
				if(!SCL)
					begin
						link_write						<= NO;
						link_sda							<= YES;
						link_head						<= YES;
						head_state						<= head_bit;
					end
				else
					head_state							<= head_begin;
			head_bit:
				if(SCL)
					begin
						FF									<= 1;
						head_buf							<= head_buf<<1;
						head_state						<= head_end;
					end
				else
					head_state							<= head_bit;
			head_end:
				if(!SCL)
					begin
						link_head						<= NO;
						link_write						<= YES;
					end
				else
					head_state							<= head_end;
		endcase
	end
endtask

//----------输出停止信号任务----------------------------------
task shift_stop;
	begin
		casex(stop_state)
			stop_begin:
				if(!SCL)
					begin
						link_sda							<= YES;
						link_write						<= NO;
						link_stop						<= YES;
						stop_state						<= stop_bit;
					end
				else
					stop_state							<= stop_begin;
			stop_bit:
				if(SCL)
					begin
						stop_buf							<= stop_buf<<1;
						stop_state						<= stop_end;
					end
				else
					stop_state							<= stop_bit;
			stop_end:
				if(!SCL)
					begin
						link_head						<= NO;
						link_stop						<= NO;
						link_sda							<= NO;
						FF									<= 1;
					end
				else
					stop_state							<= stop_end;
		endcase
	end
endtask
endmodule

//----------eeprom_Wr.v文件结束----------------------------
//----------signal.v的开始----------------------------
`timescale 1ns/1ns
`define timeslice 200
module Signal(RESET,CLK,RD,WR,ADDR,ACK,DATA);
output RESET;						//复位信号
output CLK;							//时钟信号
output RD,WR;						//读写信号
output[10:0] ADDR;				//11位地址信号
input ACK;							//读写周期的应答信号
inout[7:0] DATA;					//数据线

reg RESET;
reg CLK;
reg RD,WR;
reg W_R;								//低位:写操作;高位:读操作
reg[10:0] ADDR;
reg[7:0] data_to_eeprom;
reg[10:0] addr_mem[0:255];
reg[7:0] data_mem[0:255];
reg[7:0] ROM[1:2047];
integer i,j;
integer OUTFILE;
parameter test_number = 50;
assign DATA = (W_R)? 8'bz : data_to_eeprom;

//----------时钟信号输入-------------------------------
always #(`timeslice/2)
	CLK=~CLK;
	
//----------读写信号输入-------------------------------
initial
	begin
		RESET = 1;
		i = 0;
		j = 0;
		W_R = 0;
		CLK = 0;
		RD = 0;
		WR = 0;
		#1000;
		RESET = 0;
		repeat(test_number)						//连续写test_number次数据,调试成功后可以增加到全部地址并覆盖测试
			begin
				#(5*`timeslice);
				WR=1;
				#(`timeslice);
				WR=0;
				@(posedge ACK);					//EEPROM_WR转换模块请求写数据
			end
		#(10*`timeslice);
		W_R = 1;										//开始读操作
		repeat(test_number)						//连续读test_number次数据
			begin
				#(5*`timeslice);
				RD = 1;
				#(`timeslice);
				RD = 0;
				@(posedge ACK);					//EEPROM_WR转换模块请求读数据
			end
	end
	
//----------写操作-----------------------------------
initial
	begin
		$display("writing-----writing-----writing-----writing");
		#(2*`timeslice);
		for(i=0;i<=test_number;i=i+1)
			begin
				ADDR = addr_mem[i];				//输出写操作的地址
				data_to_eeprom = data_mem[i]; //输出需要转换的平行数据
				$fdisplay(OUTFILE,"@%0h %0h",ADDR,data_to_eeprom);	//把输出的地址和数据记录在已经打开的eeprom.dat文件中
				@(posedge ACK);					//EEPROM_WR转换模块请求写数据
			end
	end
	
//----------读操作-----------------------------------
initial
	@(posedge W_R)
	begin
		ADDR = addr_mem[0];
		$fclose(OUTFILE);							//关闭已经打开的eeprom.dat文件
		$readmemh("./eeprom.dat",ROM);		//把数据文件的数据读到ROM中
		
		$display("Begin READING-----READING-----READING-----READING");
		for(j=0;j<=test_number;j=j+1)
			begin
				ADDR = addr_mem[j];
				@(posedge ACK);
				if(DATA==ROM[ADDR])				//比较并显示发送的数据和接收到的数据是否一致
					$display("DATA%0h==ROM[%0h]---READ RIGHT",DATA,ADDR);
				else
					$display("DATA%0h!=ROM[%0h]---READ WRONG",DATA,ADDR);
			end
	end
	
initial
	begin
		OUTFILE = $fopen("./eeprom.dat");	//打开一个名为eeprom.dat的文件备用
		$readmemh("./addr.dat",addr_mem);	//把地址数据存入地址存储器
		$readmemh("./data.dat",data_mem);	//把准备写入EEPROM的数据存入数据存储器
	end

endmodule

//----------signal.v的结束----------------------------
//----------top.v文件的开始----------------------------
`include"./Signal.v"
`include"./EEPROM.v"
`include"./EEPROM_WR.v"		//可以用EEPROM_WR模块相应的Verilog门级网表来替换
`timescale 1ns/1ns
`define timeslice 200

module Top;
wire RESET;
wire CLK;
wire RD,WR;
wire ACK;
wire[10:0] ADDR;
wire[7:0] DATA;
wire SCL;
wire SDA;
parameter test_numbers = 123;
initial #(`timeslice*180*test_numbers)$stop;
Signal #(test_numbers) signal(.RESET(RESET),.CLK(CLK),.RD(RD),.WR(WR),.ADDR(ADDR),.ACK(ACK),.DATA(DATA));
EEPROM_WR eeprom_wr(.RESET(RESET),.SDA(SDA),.SCL(SCL),.ACK(ACK),.CLK(CLK),.WR(WR),.RD(RD),.ADDR(ADDR),.DATA(DATA));
EEPROM eeprom(.sda(SDA),.scl(SCL));

endmodule

//----------top.v文件的结束----------------------------
;