基于verilog的EEPROM读写
来源:互联网 发布:c语言初始化顺序表 编辑:程序博客网 时间:2024/05/16 15:21
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL
这里以一个byte的读写为例
时序图:
写时序
读时序
通过时序图可知,IIC读的时候需要先完成写的控制字和地址的命令,因为读的部分和写的部分有重复,所以这里的IIC控制模块我使用状态机来完成的,划分状态时,写时序的start—ack_low_addr这段为复用状态
控制命令和状态的定义
//instruparameter WR_CTRL_DATA= 8'hA0, HIGH_DATA = 8'h00, LOW_DATA = 8'h05, DATA_WR = 8'hAf, RD_CTRL_DATA= 8'hA1; //stateparameter WR_START = 15'b000_0000_0000_0001, WR_CTRL_BYTE= 15'b000_0000_0000_0010, ACK_WR_CTRL = 15'b000_0000_0000_0100, HIGH_ADDR = 15'b000_0000_0000_1000, ACK_HIGH = 15'b000_0000_0001_0000, LOW_ADDR = 15'b000_0000_0010_0000, ACK_LOW = 15'b000_0000_0100_0000, WR_DATA = 15'b000_0000_1000_0000, ACK_WR = 15'b000_0001_0000_0000, RD_START = 15'b000_0010_0000_0000, RD_CTRL_BYTE= 15'b000_0100_0000_0000, ACK_RD_CTRL = 15'b000_1000_0000_0000, RD_DATA = 15'b001_0000_0000_0000, NO_ACK = 15'b010_0000_0000_0000, STOP = 15'b100_0000_0000_0000;
用两个按键控制写和读
//----------key_flag-------------------- always @(posedge clk or negedge rst_n) if(!rst_n) key_wr_state <= 1'b0; else if(w_valid) key_wr_state <= 1'b1; else if(key_clr) key_wr_state <= 1'b0; else key_wr_state <= key_wr_state; always @(posedge clk or negedge rst_n) if(!rst_n) key_rd_state <= 1'b0; else if(r_valid) key_rd_state <= 1'b1; else if(key_clr) key_rd_state <= 1'b0; else key_rd_state <= key_rd_state;
iic的scl这里用的是200K
//------scl 200K------------------------------------ always @(posedge clk or negedge rst_n) if(!rst_n) cnt_clk <= 'd0; else if(key_wr_state || key_rd_state)begin if(cnt_clk == 'd124) cnt_clk <= 'd0; else cnt_clk <= cnt_clk + 1'b1; end else cnt_clk <= 'd0; always @(posedge clk or negedge rst_n) if(!rst_n) eeprom_scl <= 1'b1; else if((key_wr_state || key_rd_state) && cnt_clk == 'd124) eeprom_scl <= ~eeprom_scl; else eeprom_scl <= eeprom_scl;
状态开始信号由按键标志产生
//-------------start------------------- always @(posedge clk or negedge rst_n) if(rst_n == 1'b0) wr_start <= 1'b0; else if(w_valid || r_valid) wr_start <= 1'b1; else if(eeprom_scl == 1 && eeprom_sda == 1'b0 && cnt_clk == 'd124) wr_start <= 1'b0; else wr_start <= wr_start;
控制字命令的
//此处建议使用case语句//参考alw @(posedge clk or negedge rst_n) instru <= 'd0;else begin case(state) WR_CTRL_BYTE : HIGH_ADDR : LOW_ADDR : WR_DATA : RD_CTRL_BYTE : default : endcaseend //-----------------instru--------------------- always @(posedge clk or negedge rst_n) if(!rst_n) instru <= 'd0; else if(state == WR_CTRL_BYTE) instru <= WR_CTRL_DATA; else if(state == HIGH_ADDR) instru <= HIGH_DATA; else if(state == LOW_ADDR) instru <= LOW_DATA; else if(state == WR_DATA) instru <= DATA_WR; else if(state == RD_CTRL_BYTE) instru <= RD_CTRL_DATA; else instru <= instru;
移位计数器:因为iic需要从机ack应答
所以这里移位计数器设置9位,1到8位为串行命令的移位输入,第9位为从机应答
//--------------shift_byte----------------------- always @(posedge clk or negedge rst_n) if(!rst_n) shift_cnt <= 'd0; else if(shift_flag && state != RD_START && state != STOP)begin if(shift_cnt== 'd8 &&eeprom_scl == 'd0 && cnt_clk == 'd63) shift_cnt <= 'd0; else if(state != WR_START && eeprom_scl == 'd0 && cnt_clk == 'd63) shift_cnt <= shift_cnt + 'b1; end else shift_cnt <=shift_cnt; always @(posedge clk or negedge rst_n) if(!rst_n) shift_byte <= 'd0; else if(shift_cnt == 'd0) shift_byte <= instru; else if(shift_cnt != 'd0 && cnt_clk == 'd64 && eeprom_scl == 'd0) shift_byte <= {shift_byte[6:0],shift_byte[7]}; else shift_byte <= shift_byte;
当写控制完成后产生stop信号,然后需要读的时候这里设置了读数据的开始信号
//-------------------rd_start_flag-------------- always @(posedge clk or negedge rst_n) if(!rst_n) rd_start_flag <= 'd0; else if(key_rd_state && state == ACK_LOW && eeprom_scl == 'd0 && cnt_clk == 'd63) rd_start_flag <= 'd1; else rd_start_flag <= 'd0;
写状态完成和读状态完成后都会产生一个stop标志回到程序复位状态等待下次读写
//------------------stop_wr----------------------------- always @(posedge clk or negedge rst_n) if(!rst_n) stop_wr <= 'd0; else if((state == ACK_WR||state == NO_ACK) && eeprom_scl =='d0&& cnt_clk == 'd63) stop_wr <= 'd1; else stop_wr <= 'd0;
因为iic需要从机应答,所以仿真时应答信号可以设置为高阻态
//---------eeprom_sda---------------------------------------- always @(posedge clk or negedge rst_n) if(rst_n == 1'b0) oe <= 1'b1; else if((shift_cnt =='d8 && state != NO_ACK) || state ==RD_DATA)// oe <= 1'b0; else oe <= 1'b1; assign eeprom_sda = (oe == 1'b1)? sda_r: 1'bz;
状态机
wire key_clr; assign key_clr = (state == STOP && eeprom_scl == 'd1 && cnt_clk == 'd123)? 1'b1:1'b0; wire state_jump_flag; assign state_jump_flag = (eeprom_scl == 0 && cnt_clk == 'd63 && shift_cnt=='d7)? 1'b1:1'b0; wire ack_jump; assign ack_jump = (shift_cnt=='d8 && eeprom_scl == 0 && cnt_clk == 'd63)? 1'b1:1'b0;//----------------state_ctrl------------------------------ always @(posedge clk or negedge rst_n) if(!rst_n) state <= WR_START; else begin case(state) //1-------------------------wr_state---------------------- WR_START : if(shift_flag && eeprom_scl == 0 && cnt_clk == 'd63) state <= WR_CTRL_BYTE; else state <= WR_START; //2 WR_CTRL_BYTE : if(state_jump_flag) state <= ACK_WR_CTRL; else state <= WR_CTRL_BYTE; //4 ACK_WR_CTRL : if(ack_jump) state <= HIGH_ADDR; else state <= ACK_WR_CTRL; //8 HIGH_ADDR : if(state_jump_flag) state <= ACK_HIGH; else state <= HIGH_ADDR; //10 ACK_HIGH : if(ack_jump) state <= LOW_ADDR; else state <= ACK_HIGH; //20 LOW_ADDR : if(state_jump_flag) state <= ACK_LOW; else state <= LOW_ADDR; //40 ACK_LOW : if(ack_jump)begin if(key_wr_state) state <= WR_DATA; else if(key_rd_state) state <= RD_START; end else state <= ACK_LOW; // WR_DATA : if(state_jump_flag) state <= ACK_WR; else state <= WR_DATA; // ACK_WR : if(ack_jump) state <= STOP; else state <= ACK_WR; //---------------------rd_state-------------------------- RD_START : if(shift_flag && eeprom_scl == 0 && cnt_clk == 'd63) state <= RD_CTRL_BYTE; else state <= RD_START; RD_CTRL_BYTE : if(state_jump_flag) state <= ACK_RD_CTRL; else state <= RD_CTRL_BYTE; ACK_RD_CTRL : if(ack_jump) state <= RD_DATA; else state <= ACK_RD_CTRL; RD_DATA : if(state_jump_flag) state <= NO_ACK; else state <= RD_DATA; NO_ACK : if(ack_jump) state <= STOP; else state <= NO_ACK; //-------------------wr_rd_stop------------------------------ STOP : if(eeprom_scl && cnt_clk == 'd124) state <= WR_START; else state <= STOP; default : state <= WR_START; endcase end
读数据串行数据转换
//-----------------RD_DATA-------------------------- always @(posedge clk or negedge rst_n) if(!rst_n) DATA_r <= 8'd0; else if(state == RD_DATA)begin case(shift_cnt) 4'd0 : DATA_r[7] <= eeprom_sda; 4'd1 : DATA_r[6] <= eeprom_sda; 4'd2 : DATA_r[5] <= eeprom_sda; 4'd3 : DATA_r[4] <= eeprom_sda; 4'd4 : DATA_r[3] <= eeprom_sda; 4'd5 : DATA_r[2] <= eeprom_sda; 4'd6 : DATA_r[1] <= eeprom_sda; 4'd7 : DATA_r[0] <= eeprom_sda; default : DATA_r <= 8'd0; endcase end always @(posedge clk or negedge rst_n) if(!rst_n) data <= 8'd0; else if(shift_cnt == 'd7 && eeprom_scl == 1'b0 && cnt_clk == 'd63) data <= DATA_r; else data <= data;
整个控制模块基本完成了
下面看一下仿真
这里用singtap II 抓取 了波形
0 0
- 基于verilog的EEPROM读写
- EEPROM的verilog HDL程序
- eeprom 的读写
- stc90c52rc的eeprom读写
- stm8s103f3p EEPROM的读写
- AT24C128 EEPROM的读写
- STC89C52RC内部EEPROM的读写
- STC89C52RC内部EEPROM的读写
- PIC16F877A单片机的EEPROM读写
- eeprom读写
- 基于mini6410平台:uboot添加支持IIC总线的eeprom读写
- 基于verilog的交通灯
- EEPROM读写操作常见的陷阱
- EEPROM读写操作常见的陷阱
- EEPROM读写操作常见的陷阱
- STC单片机片内EEPROM的读写
- STM32L系列单片机内部EEPROM的读写
- STM32单片机内部EEPROM的读写
- 【Linux网络编程】UDP编程
- 搭建storm集群
- 笔记:CXF与spring整合
- 面试前的思考
- UVA 11361 - Investigating Div-Sum Property-数位DP
- 基于verilog的EEPROM读写
- 唯品会订单分库分表的实践总结以及关键步骤
- 程序员-前行-001-c++程序
- X86 内存布局分析(Memory map)
- jap 复合查询
- oracle存储过程和存储函数(1)
- numpy入门2
- 多年之痒,终结于今日
- Android ORM框架介绍之android-liteorm(archiver)注解与封装