赞
踩
FPGA I2C接口实现
计科210X wolf 202108010XXX
报告内容将包括:
(1)如何描述组合电路、时序电路、状态机?如何编写TestBench?
(2)ModelSim工具的使用;
(3)EEPROM读写代码分析;
(4)实验总结;
注意:其中(1)将在自定FSM中以实例的方式呈现,(2)将在(3)中提及。
附件包括:
(1)FSM_example(文件夹),这是自定FSM的文件
(2)I2C(文件夹),这里放的是EEPROM的相关文件,其子文件夹包括
I2C_Rtl目录:存放i2c.v(设计文件);
I2C_Tes目录:存放i2c_tb.v(测试文件)和EEPROM的模型文件(在仿真时用模型文件代替实际芯片,M24XXX_Macro.v,M24XXX_Memory.v,M24XXX_Parameters.v,请在“资料”栏中下载);
I2C_Sim目录:工程目录,用于存放工程文件等;
一、自定FSM说明
1、状态描述
设计如下状态,表示单日活动轨迹:
S0:宿舍休息
S1:起床并吃早餐,吃完后有课则上课(a=1),无课自习,
S2:12节上课,若34有课则继续去上课(b=1),否则自习
S3:12节自习,若34有课则去上课(b=1),否则自习
S4:34节上课,结束后去吃午餐
S5:34节自习,结束后去吃午餐
S6:吃午餐,午餐后有工训课就上工训课(c=1),否则午休
S7:午休,若56节有课(d=1)则去上课,否则去图书馆阅读
S8:中午工训课,若56节有课(d=1)则去上课,否则去图书馆阅读
S9:56节上课,若78有课则继续去上课(e=1),否则若晴天(f=1)则运动,雨天则自习
S10:56节阅读,若78有课则继续去上课(e=1),否则若晴天(f=1)则运动,雨天则自习
S11:78节上课,结束后去吃晚餐
S12:78节运动,结束后去吃晚餐
S13:78节自习(教学楼),结束后去吃晚餐
S14:晚餐,吃完后自习,若周五直接回寝室(g=1)
S15:教学楼晚自习,结束后直接回寝休息
输出pos为位置:
00:宿舍园区
01:教学楼
10:体育场
11:图书馆
状态图如下:
2、设计代码说明
(1)状态机描述代码如下:
- //输入端口为控制信号和时钟信号,输出端口为pos,表示位置
- module fsm_example(
- input clk;
- input a,b,c,d,e,f,g,
- output reg [1:0] pos
- )
-
- //使用4位16进制数表示所有状态
- parameter s0=4'h0,s1=4'h1,s2=4'h2,s3=4'h3,s4=4'h4,s5=4'h5,s6=4'h6,s7=4'h7,s8=4'h8,s9=4'h9,s10=4'ha,s11=4'hb,s12=4'hc,s13=4'hd,s14=4'he,s15=4'hf;
- reg [3:0] state,next_state;
- //下一状态判断
- always @(*) begin
- case (state)
- s0: next_state=s1;
- s1: if(a) next_state=s2;
- else next_state=s3;
- s2: if(b) next_state=s4;
- else next_state=s5;
- s3: if(b) next_state=s4;
- else next_state=s5;
- s4: next_state=s6;
- s5: next_state=s6;
- s6: if(c) next_state=s8;
- else next_state=s7;
- s7: if(d) next_state=s9;
- else next_state=s10;
- s8: if(d) next_state=s9;
- else next_state=s10;
- s9: if(e) next_state=s11;
- else begin
- if(f) next_state=s12;
- else next_state=s13;
- end
- s10: if(e) next_state=s11;
- else begin
- if(f) next_state=s12;
- else next_state=s13;
- end
- s11: next_state=s14;
- s12: next_state=s14;
- s13: next_state=s14;
- s14: if(g) next_state=15;
- else next_state=s0;
- s15: next_state=s0;
- default: next_state=s0;
- endcase
- end
- //状态更新与输出
- always @(posedge clk) state<=next_state;
-
- always @(*) begin
- case (state)
- s0: pos=2'b00;
- s1: pos=2'b00;
- s2: pos=2'b01;
- s3: pos=2'b01;
- s4: pos=2'b01;
- s5: pos=2'b01;
- s6: pos=2'b00;
- s7: pos=2'b00;
- s8: pos=2'b01;
- s9: pos=2'b01;
- s10: pos=2'b11;
- s11: pos=2'b01;
- s12: pos=2'b10;
- s13: pos=2'b01;
- s14: pos=2'b00;
- s15: pos=2'b01;
- default: pos=2'b00;
- endcase
- End
-
- endmodule
(2)test_bench代码如下:
- `timescale 1ns/100ps
- module xpos_tb();
-
- parameter s0=4'h0,s1=4'h1,s2=4'h2,s3=4'h3,s4=4'h4,s5=4'h5,s6=4'h6,s7=4'h7,s8=4'h8,s9=4'h9,s10=4'ha,s11=4'hb,s12=4'hc,s13=4'hd,s14=4'he,s15=4'hf;
- reg [3:0] state,next_state;
- reg a,b,c,d,e,f,g;
- wire [1:0] pos;
- pos xpos(clk,a,b,c,d,e,f,g,pos);
- initial clk=0;
- always #50 clk=~clk;
- initial begin
- a=0;b=0;c=0;d=0;e=0;f=0;g=0;
- #1
- #400
- a=1;
- b=1;
- c=1;
- d=0;
- e=0;
- f=1;
- g=0;
- #600
- repeat(1024) @(posedge clk);
- $stop;
- end
- endmodule
第一次从s0开始,设定状态变化为:
s0-> s1->s2->s4->s6->s8->s10->s12->s14->s15->s0
二、EEPROM读写代码设计及仿真
1、代码说明
输入:
clk,rstn分别为时钟和复位信号
write_op:写命令,低电平有效
write_data:写数据
addr:地址
read_op:读命令,低电平有效
输出:
read_data:读到的数据
op_done:操作结束
I2C协议信号:
scl:I2C协议的scl信号
sda:I2C协议的sda信号
使用8位16进制数表示所有状态,共55个:
scl周期是使用计数器对时钟周期计数实现的,一个scl周期是30个时钟周期,30*200k=6Mhz,为所使用的FPGA板的时钟频率。
下一状态的更新:
使用wr_op和rd_op将输入信号write_op,read_op表示的读写命令用高电平表示:
下一状态判断,与状态图一致,时间为scl_tick,即scl周期结束。
首先是在scl=1时,sda由1->0,开始数据传输,并先写入器件地址(10100000)和数据地址,然后根据wr_op,rd_op判断进行读还是写操作,写操作直接开始写入数据,读操作则需要重新写器件地址和数据地址,然后读取数据。
以下是下一状态判断部分的代码
SCL同步的实现:
空闲,等待,操作结束,start开始等状态下SCL都是高电平,因此不需要clr_scl对SCL清零。另外clr_scl只在scl_ls(scl的低电平开始)处才置1,把scl清0,在15个clk周期的scl_hs处,再把scl拉高,就实现了SCL周期。
SDA:
SDA的控制信号声明,这些信号在对应的状态且scl在低电平的中心时置1,根据这些控制信号,在SDA上进行数据读写。而i2c_reg用来暂存数据。
使用信号i2c_rlf表示是否有读写操作,如果有,则i2c_reg将左移,一位一位处理数据。
根据上述控制信号,将输入的特定数据保存到i2c_reg
sda输出使用sda使能信号sda_en控制,写器件地址,数据地址,写数据时使能信号为1,接收ACK响应时使能为0。sda输出i2c_reg的最高位,即一位一位完成读或写。
读取数据时将数据读到read_data
最后使用d5ms_count计数时钟周期等待,使用时钟频率为6Mhz,一个周期166ns,等待约1.36ms,然后重新开始完成新的读写命令。
2、TestBench代码说明
模块声明与实例化:
根据时钟频率6Mhz设置周期166ns,并对信号初始化:
首先输入写命令信号,地址为8‘h55,写入的数据为8’haa。等待操作完成后,将write_op设为1(高电平无效),输入读命令信号,读出地址8’h55中的数据,读出的数据应该为刚刚写入的8’haa。
3、仿真波形说明(截图+文字标注)
首先是向地址为8‘h55处写入数据,sda在scl为高电平时产生下降沿,表示开始工作,scl开始翻转,依次写入器件地址,数据地址,以及数据8’haa,并接收响应。最后scl为高电平,sda产生上升沿,停止工作。
经过等待后开始读出0x55处的数据,先写入器件地址,数据地址(dummy write),然后再次写入器件地址,读出数据。
按照教师要求,利用Quartus分配管脚,按下图实物图指示分配管脚,Quartus管脚分配截图如下。
Quartus管脚分配:
Quartus下载:
下板验证:
下板验证解读:
三、实验总结
通过本次实验,我学习到了许多知识,同时也暴露了一些问题。
我详细学习了verilog语言的基本语法以及使用verilog语言描述时序逻辑和组合逻辑的方式。同时,我也了解了如何使用verilog编写有限状态机并练习编写了简单的有限状态机。了解了test_bench的编写以及熟悉了使用ModelSim进行波形仿真。理解了I2C接口协议以及I2C协议下SCL,SDA数据是如何传输的。我详细深入地研究了I2C协议,对于给出的参考代码能够基本理解,也能够对应波形仿真结果解释I2C协议的数据传输,最后将代码下载至FPGA开发板,验证了I2C协议正常工作。
但是,我对于实验中I2C协议的verilog实现的一些具体细节理解的还不够深刻,使用verilog语言编写有限状态机的能力还比较基础,还需要后续的练习和进一步的学习。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。