当前位置:   article > 正文

Verilog复杂时序逻辑电路设计实践_verilog时序逻辑实例

verilog时序逻辑实例

笔试时也很常见。

[例1] 一个简单的状态机设计--序列检测器

序列检测器是时序数字电路设计中经典的教学范例,下面我们将用Verilog HDL语言来描述、仿真、并实现它。

序列检测器的逻辑功能描述:

序列检测指的就是将一个指定的序列从数字码流中识别出来。本例中,我们将设计一个“10010”序列的检测器。设X为数字码流输入,Z为检出标记输出,高电平表示“发现指定序列”,低电平表示“没有发现指定序列”。考虑码流为“ 110010010000100101…” 则有下表:

时钟12345678910111213141516171819
X110010010000100101
Z000001001000000010

在时钟2-6,码流X中出现指定序列“10010”,对应输出Z在第6个时钟变为高电平――“1”,表示“发现指定序列”。同样地,在时钟13-17码流,X中再次出现指定序列“10010”,Z输出“1”。注意,在时钟5-9还有一次检出,但它是与第一次检出的序列重叠的,即前者的前面两位同时也是后者的最后两位。

根据以上逻辑功能描述,我们可以分析得出状态转换图如下:

其中状态A-E表示5比特序列“10010”按顺序正确地出现在码流中。考虑到序列重叠的可能,转换图中还有状态F、G。另外、电路的初始状态设为IDLE。

进一步,我们得出Verilog HDL代码。

  1. //文件:sequence.v
  2. module seqdet( x, z, clk, rst);
  3. input x,clk, rst;
  4. output z;
  5. reg [2:0] state;//状态寄存器
  6. wire z;
  7. parameter  IDLE= 'd0, A='d1,  B='d2,
  8.      C='d3,  D='d4,
  9.      E='d5,  F='d6,
  10.       G='d7;
  11. assign z=(state==D && x==0) ? 1 :0;
  12. always @(posedge clk or negedge rst)
  13.  if(!rst)
  14.   begin
  15.   state<=IDLE;
  16.   
  17.   end
  18.  else
  19.   casex( state)
  20.    IDLE: if(x==1)
  21.       begin
  22.        state<=A;
  23.       end
  24.    A:    if (x==0)
  25.       begin
  26.        state<=B;
  27.       end
  28.    B: if (x==0)
  29.       begin 
  30.        state<=C;
  31.       end
  32.      else
  33.       begin
  34.        state<=F;
  35.       end
  36.    C: if(x==1)
  37.       begin
  38.        state<=D;
  39.       end
  40.          else
  41.       begin
  42.        state<=G;
  43.       end
  44.    D: if(x==0)
  45.       begin
  46.        state<=E;
  47.       end
  48.          else
  49.       begin
  50.        state<=A;
  51.       end
  52.    E: if(x==0)
  53.       begin
  54.        state<=C;
  55.       end
  56.          else
  57.       begin
  58.        state<=A;
  59.       end
  60.    F:  if(x==1)
  61.       begin
  62.        state<=A;
  63.       end
  64.          else
  65.       begin
  66.        state<=B;
  67.       end
  68.    G:      if(x==1)
  69.       begin
  70.        state<=F;
  71.       end
  72.    default:  state<=IDLE;
  73.    endcase
  74. endmodule

为了验证其正确性,我们接着编写测试用代码。

  1. //文件:sequence.tf
  2. `timescale 1ns/1ns
  3. module t;
  4. reg clk, rst;
  5. reg [23:0] data;
  6. wire z,x;
  7. assign x=data[23];
  8. initial
  9.  begin
  10.   clk<=0;
  11.   rst<=1;
  12.   #2 rst<=0;
  13.   #30 rst<=1;  //复位信号
  14.   data='b1100_1001_0000_1001_0100; //码流数据
  15.  end
  16. always #10 clk=~clk; //时钟信号
  17. always @ (posedge clk) // 移位输出码流
  18.   data={data[22:0],data[23]};
  19.     
  20. seqdet m ( .x(x), .z(z), .clk(clk), .rst(rst)); //调用序列检测器模块
  21. // Enter fixture code here
  22. endmodule // t

其中、X码流的产生,我们采用了移位寄存器的方式,以方便更改测试数据。仿真结果如下图所示:

从波形中,我们可以看到程序代码正确地完成了所要设计的逻辑功能。另外,sequence.v的编写,采用了可综合的Verilog HDL 风格,它可以通过综合器的综合最终实现到FPGA中。

说明:以上编程、仿真、综合和后仿真在PC WINDOWS NT 4.0操作系统及QuickLogic SPDE环境下通过。

[例2]EEPROM读写器件的设计

下面我们将介绍一个经过实际运行验证并可综合到各种FPGA和ASIC工艺的串行EEPROM读写器件的设计过程。列出了所有有关的Verilog HDL程序。这个器件能把并行数据和地址信号转变为串行EEPROM能识别的串行码并把数据写入相应的地址,或根据并行的地址信号从EEPROM相应的地址读取数据并把相应的串行码转换成并行的数据放到并行地址总线上。当然还需要有相应的读信号或写信号和应答信号配合才能完成以上的操作。

1. 二线制I2C CMOS 串行EEPROM的简单介绍

二线制I2C CMOS 串行EEPROM AT24C02/4/8/16 是一种采用CMOS 工艺制成的串行可用电擦除可编程只读存储器。串行EEPROM 一般具有两种写入方式,一种是字节写入方式,还有另一种页写入方式,允许在一个写周期内同时对一个字节到一页的若干字节进行编程写入,一页的大小取决于芯片内页寄存器的大小,不同公司的同一种型号存储器的内页寄存器可能是不一样的。为了程序的简单起见,在这里只编写串行 EEPROM 的一个字节的写入和读出方式的Verilog HDL 的行为模型代码,串行EEPROM读写器的Verilog HDL模型也只是字节读写方式的可综合模型,对于页写入和读出方式,读者可以参考有关书籍,改写串行EEPROM 的行为模型和串行EEPROM读写器的可综合模型。

2. I2C (Inter Integrated Circuit)总线特征介绍

I2C 双向二线制串行总线协议定义如下:

只有在总线处于“非忙”状态时,数据传输才能被初始化。在数据传输期间,只要时钟线为高电平,数据线都必须保持稳定,否则数据线上的任何变化都被当作“启动”或“停止”信号。图 1 是被定义的总线状态。

① 总线非忙状态(A 段)

数据线SDA 和 时钟线 SCL 都保持高电平。

② 启动数据传输(B 段)

当时钟线(SCL)为高电平状态时,数据线(SDA)由高电平变为低电平的下降沿被认为是“启动”信号。只有出现“启动”信号后,其它的命令才有效。

③ 停止数据传输(C 段)

当时钟线(SCL)为高电平状态时,数据线(SDA)由低电平变为高电平的上升沿被认为是“停止”信号。随着“停在”信号出现,所有的外部操作都结束。

④ 数据有效(D 段)

在出现“启动”信号以后,在时钟线(SCL)为高电平状态时数据线是稳定的,这时数据线的状态就要传送的数据。数据线(SDA)上的数据的改变必须在时钟线为低电平期间完成,每位数据占用一个时钟脉冲。每个数传输都是由“启动”信号开始,结束于“停止”信号。

⑤ 应答信号

每个正在接收数据的EEPROM 在接到一个字节的数据后,通常需要发出一个应答信号。而每个正在发送数据的EEPROM 在发出一个字节的数据后,通常需要接收一个应答信号。EEPROM 读写控制器必须产生一个与这个应答位相联系的额外的时钟脉冲。在EEPROM 的读操作中,EEPROM 读写控制器对EEPROM 完成的最后一个字节不产生应答位,但是应该给EEPROM 一个结束信号。

3. 二线制I2C CMOS 串行EEPROM读写操作

1) EEPROM 的写操作(字节编程方式)

所谓EEPROM的写操作(字节编程方式)就是通过读写控制器把一个字节数据发送到EEPROM 中指定地址的存储单元。其过程如下:EEPROM 读写控制器发出“启动”信号后,紧跟着送4位I2C总线器件特征编码1010 和3 位EEPROM 芯片地址/页地址XXX 以及写状态的R/W 位(=0),到总线上。这一字节表示在接收到被寻址的EEPROM 产生的一个应答位后,读写控制器将跟着发送1个字节的EEPROM 存储单元地址和要写入的1个字节数据。EEPROM 在接收到存储单元地址后又一次产生应答位以后,读写控制器才发送数据字节,并把数据写入被寻址的存储单元。EEPROM 再一次发出应答信号,读写控制器收到此应答信号后,便产生“停止”信号。字节写入帧格式如图2所示:

2) 二线制I2C CMOS 串行EEPROM 的读操作

所谓EEPROM的读操作即通过读写控制器读取 EEPROM 中指定地址的存储单元中的一个字节数据。串行EEPROM 的读操作分两步进行:读写器首先发送一个“启动”信号和控制字节(包括页面地址和写控制位)到EEPROM,再通过写操作设置EEPROM 存储单元地址(注意:虽然这是读操作,但需要先写入地址指针的值),在此期间EEPROM 会产生必要的应答位。接着读写器重新发送另一个“启动”信号和控制字节(包括页面地址和读控制位R/W = 1),EEPROM收到后发出应答信号,然后,要寻址存储单元的数据就从SDA 线上输出。读操作有三种:读当前地址存储单元的数据、读指定地址存储单元的数据、读连续存储单元的数据。在这里只介绍读指定地址存储单元数据的操作。读指定地址存储单元数据的帧格式如图3:

4. EEPROM的Verilog HDL 程序

要设计一个串行EEPROM读写器件,不仅要编写EEPROM读写器件的可综合Verilog HDl的代码,而且要编写相应的测试代码以及EERPOM的行为模型。EEPROM的读写电路及其测试电路如图4。

1) EEPROM的行为模型

为了设计这样一个电路我们首先要设计一个EEPROM的Verilog HDL模型,而设计这样一个模型我们需要仔细地阅读和分析EEPROM器件的说明书,因为EEPROM不是我们要设计的对象,而是我们验证设计对象所需要的器件,所以只需设计一个EEPROM的行为模型,而不需要可综合风格的模型,这就大大简化了设计过程。下面的Verilog HDL程序就是这个EEPROM(AT24C02/4/8/16) 能完成一 个字节数据读写的部分行为模型,请读者查阅AT24C02/4/8/16说明书,对照下面的Verilog HDL程序理解设计的要点。因为这一程序是我们自己编写的有不完善之处敬请指正。

这里只对在操作中用到的信号线进行模拟,对于没有用到的信号线就略去了。对EEPROM用于基本总线操作的引脚SCL和SDA说明如下:SCL,串行时钟端,这个信号用于对输入和输出数据的同步,写入串行EEPROM的数据用其上升沿同步,输出数据用其下降沿同步;SDA,串行数据(/地址)输入/输出端。

  1. `timescale 1ns/1ns
  2. `define timeslice 100
  3. module EEPROM(scl, sda);   
  4. input  scl;    //串行时钟线
  5. inout  sda;    //串行数据线
  6. reg out_flag;  //SDA数据输出的控制信号
  7. reg[7:0]  memory[2047:0];
  8. reg[10:0] address; 
  9. reg[7:0]  memory_buf;
  10. reg[7:0]  sda_buf;   //SDA 数据输出寄存器
  11. reg[7:0]  shift;     //SDA 数据输入寄存器
  12. reg[7:0]  addr_byte; //EEPROM 存储单元地址寄存器
  13. reg[7:0]  ctrl_byte; //控制字寄存器
  14. reg[1:0]  State;     //状态寄存器
  15. integer i;
  16. //--------------------------------------------------------------
  17. parameter    r7= 8'b10101111,w7= 8'b10101110,         //main7
  18.           r6= 8'b10101101,w6= 8'b10101100,         //main6
  19.            r5= 8'b10101011,w5= 8'b10101010,         //main5
  20.           r4= 8'b10101001,w4= 8'b10101000,         //main4
  21.            r3= 8'b10100111,w3= 8'b10100110,         //main3
  22.            r2= 8'b10100101,w2= 8'b10100100,         //main2
  23.            r1= 8'b10100011,w1= 8'b10100010,         //main1
  24.           r0= 8'b10100001,w0= 8'b10100000;         //main0
  25. //--------------------------------------------------------------
  26. assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz; 
  27. //―――――――寄存器和存储器初始化―――――――――――――――
  28. initial
  29.   begin
  30.     addr_byte     = 0;
  31.     ctrl_byte     = 0; 
  32.     out_flag      = 0;
  33.     sda_buf       = 0;
  34.     State         = 2'b00;
  35.     memory_buf    = 0;
  36.     address       = 0;
  37.     shift         = 0;
  38.     for(i=0;i<=2047;i=i+1)
  39.       memory[i]=0;
  40.   end
  41. //------------ 启动信号 -----------------------------
  42. always @ (negedge sda)  
  43.   if(scl == 1 )
  44.     begin
  45.       State = State + 1;
  46.       if(State == 2'b11)
  47.          disable write_to_eeprm;
  48.     end
  49. //------------ 主状态机 --------------------------
  50. always @(posedge sda)            
  51.   if (scl == 1 )      //停止操作         
  52.     stop_W_R;
  53.   else 
  54.     begin
  55.      casex(State)
  56.          2'b01:
  57.              begin  
  58.                read_in;
  59.                    if(ctrl_byte==w7||ctrl_byte==w6||ctrl_byte==w5
  60.                       ||ctrl_byte==w4||ctrl_byte==w3||ctrl_byte==w2
  61.                       ||ctrl_byte==w1||ctrl_byte==w0)
  62.                begin
  63.                  State = 2'b10;
  64.                  write_to_eeprm;  //写操作 
  65.                 end
  66.             else
  67.               State = 2'b00;
  68.           end
  69.  
  70.          2'b11:    
  71.                read_from_eeprm;      //读操作            
  72.      default:  
  73.                State=2'b00;  
  74.      
  75.    endcase
  76.   end
  77. //------------- 操作停止------------------------------
  78. task stop_W_R;
  79.   begin
  80.     State =2'b00;  //状态返回为初始状态
  81.     addr_byte = 0;
  82.     ctrl_byte = 0;
  83.     out_flag  = 0;
  84.     sda_buf   = 0;
  85.   end
  86. endtask
  87. //------------- 读进控制字和存储单元地址 ------------------------
  88. task  read_in;
  89.   begin
  90.     shift_in(ctrl_byte);
  91.     shift_in(addr_byte);   
  92.   end
  93. endtask
  94. //------------EEPROM 的写操作---------------------------------------
  95. task write_to_eeprm;
  96.   begin
  97.     shift_in(memory_buf);    
  98.     address          = {ctrl_byte[3:1],addr_byte};
  99.     memory[address]  = memory_buf; 
  100.     $display("eeprm----memory[%0h]=%0h",address,memory[address]);   
  101.     State =2'b00;             //回到0状态
  102.   end
  103. endtask
  104. //-----------EEPROM 的读操作----------------------------------------
  105. task read_from_eeprm;
  106.   begin 
  107.     shift_in(ctrl_byte);
  108.     if(ctrl_byte==r7||ctrl_byte==r6||ctrl_byte==r5||ctrl_byte==r4
  109.        ||ctrl_byte==r3||ctrl_byte==r2||ctrl_byte==r1||ctrl_byte==r0)
  110.       begin
  111.         address = {ctrl_byte[3:1],addr_byte};  
  112.       sda_buf = memory[address];
  113.       shift_out;
  114.       State= 2'b00;
  115.       end
  116.   end    
  117. endtask        
  118. //-----SDA 数据线上的数据存入寄存器,数据在SCL的高电平有效-------------
  119. task shift_in;
  120.  output [7:0] shift; 
  121.   begin
  122.      @ (posedge  scl) shift[7] = sda;  
  123.      @ (posedge  scl) shift[6] = sda;
  124.      @ (posedge  scl) shift[5] = sda;
  125.      @ (posedge  scl) shift[4] = sda;
  126.      @ (posedge  scl) shift[3] = sda; 
  127.      @ (posedge  scl) shift[2] = sda;
  128.      @ (posedge  scl) shift[1] = sda;
  129.      @ (posedge  scl) shift[0] = sda;
  130.      @ (negedge scl)      
  131.        begin  
  132.          #`timeslice ;
  133.         out_flag = 1;     //应答信号输出
  134.           sda_buf  = 0;  
  135.        end
  136.      @(negedge scl)
  137.         #`timeslice out_flag  = 0;  
  138.   end
  139. endtask
  140. //―――EEPROM 存储器中的数据通过SDA 数据线输出,数据在SCL 低电平时变化
  141. task shift_out;
  142.   begin
  143.     out_flag = 1;
  144.     for(i=6;i>=0;i=i-1)
  145.       begin 
  146. @ (negedge scl);
  147. #`timeslice;
  148. sda_buf = sda_buf<<1;
  149.          end
  150.     @(negedge scl)  #`timeslice sda_buf[7] = 1;  //非应答信号输出
  151.     @(negedge scl)  #`timeslice out_flag  = 0;  
  152.   end
  153. endtask     
  154.   endmodule

### 2 ) EEPROM读写器的可综合的Verilog HDL模型

下面的程序是一个串行EEPROM读写器的可综合的Verilog HDL模型,它接收来自信号源模型产生的读信号、写信号、并行地址信号、并行数据信号,并把它们转换为相应的串行信号发送到串行EEPROM(AT24C02/4/8/16) 的行为模型中去;它还发送应答信号 (ACK)到信号源模型,以便让信号源来调节发送或接收数据的速度以配合EEPROM模型的接收(写)和发送(读)数据。因为它是我们的设计对象,所以它不但要仿真正确无误,还需要可综合。

这个程序基本上由两部分组成:开关组合电路和控制时序电路,见图5。开关电路在控制时序电路的控制下按照设计的要求有节奏的打开或闭合,这样SDA可以按I2C 数据总线的格式输出或输入,SDA和SCL一起完成EEPROM的读写操作。

EEPROM读写器的结构

电路最终用同步有限状态机(FSM)的设计方法实现。程序实则上是一个嵌套的状态机,由主状态机和从状态机通过由控制线启动的总线在不同的输入信号的情况下构成不同功能的较复杂的有限状态机,这个有限状态机只有唯一的驱动时钟CLK。根据串行EEPROM的读写操作时序可知,用5个状态时钟可以完成写操作,用7个状态时钟可以完成读操作,由于读写操作的状态中有几个状态是一致的,用一个嵌套的状态机即可。状态转移如图6,程序由一个读写大任务和若干个较小的任务所组成,其状态机采用独热编码,若需改变状态编码,只需改变程序中的parameter定义即可。读者可以通过模仿这一程序来编写较复杂的可综合Verilog HDL模块程序。这个设计已通过后仿真,并可在FPGA上实现布局布线。

读写操作状态转移
  1. `timescale 1ns/1ns 
  2. module EEPROM_WR(SDA,SCL,ACK,RESET,CLK,WR,RD,ADDR,DATA);
  3. output SCL;                //串行时钟线
  4. output ACK;                //读写一个周期的应答信号
  5. input  RESET;             //复位信号
  6. input  CLK;               //时钟信号输入
  7. input  WR,RD;            //读写信号 
  8. input[10:0] ADDR;          //地址线
  9. inout SDA;                 //串行数据线
  10. inout[7:0] DATA;           //并行数据线
  11. reg ACK;
  12. reg SCL;
  13. reg WF,RF;                //读写操作标志
  14. reg FF;                    //标志寄存器
  15. reg [1:0] head_buf;         //启动信号寄存器
  16. reg[1:0] stop_buf;           //停止信号寄存器 
  17. reg [7:0] sh8out_buf;         //EEPROM写寄存器
  18. reg [8:0] sh8out_state;        //EEPROM 写状态寄存器
  19. reg [9:0] sh8in_state;         //EEPROM 读状态寄存器
  20. reg [2:0] head_state;          //启动状态寄存器
  21. reg [2:0] stop_state;          //停止状态寄存器
  22. reg [10:0] main_state;        //主状态寄存器  
  23. reg [7:0] data_from_rm;      //EEPROM读寄存器
  24. reg link_sda;               //SDA 数据输入EEPROM开关  
  25. reg link_read;              //EEPROM读操作开关   
  26. reg link_head;              //启动信号开关
  27. reg link_write;             //EEPROM写操作开关
  28. reg link_stop;              //停止信号开关 
  29. wire sda1,sda2,sda3,sda4;    
  30. //--------------串行数据在开关的控制下有次序的输出或输入-------------------
  31. assign sda1  = (link_head)     ?   head_buf[1]    :  1'b0;
  32. assign sda2  = (link_write)    ?   sh8out_buf[7]  :  1'b0;
  33. assign sda3  = (link_stop)     ?   stop_buf[1]    :  1'b0;
  34. assign sda4  = (sda1 | sda2 | sda3);
  35. assign SDA  = (link_sda)       ?  sda4            :  1'bz;
  36. assign DATA = (link_read)      ?  data_from_rm    :  8'hzz;
  37. //--------------------------------主状态机状态------------------------------------------
  38. parameter  
  39.                 Idle        = 11'b00000000001,
  40.                  Ready      = 11'b00000000010, 
  41.               Write_start   = 11'b00000000100,    
  42.                Ctrl_write   = 11'b00000001000, 
  43.                 Addr_write  = 11'b00000010000, 
  44.       Data_write   = 11'b00000100000,  
  45.       Read_start   = 11'b00001000000,     
  46.       Ctrl_read    = 11'b00010000000,      
  47.        Data_read   = 11'b00100000000,      
  48.        Stop        = 11'b01000000000,      
  49.       Ackn       = 11'b10000000000,
  50. //-------------------------并行数据串行输出状态-----------------------------
  51.                sh8out_bit7     = 9'b000000001,
  52.                sh8out_bit6     = 9'b000000010,
  53.                sh8out_bit5     = 9'b000000100,
  54.                sh8out_bit4     = 9'b000001000,  
  55.                sh8out_bit3     = 9'b000010000,
  56.                sh8out_bit2     = 9'b000100000,
  57.                sh8out_bit1     = 9'b001000000,
  58.                sh8out_bit0     = 9'b010000000,
  59.                sh8out_end     = 9'b100000000;
  60. //--------------------------串行数据并行输出状态----------------------------
  61. parameter      sh8in_begin    = 10'b0000000001,
  62.                sh8in_bit7     = 10'b0000000010,
  63.                sh8in_bit6     = 10'b0000000100,
  64.                sh8in_bit5     = 10'b0000001000,  
  65.                sh8in_bit4     = 10'b0000010000,
  66.                sh8in_bit3     = 10'b0000100000,
  67.                sh8in_bit2     = 10'b0001000000,
  68.                sh8in_bit1     = 10'b0010000000,
  69.                sh8in_bit0     = 10'b0100000000,
  70.                sh8in_end      = 10'b1000000000,
  71. //---------------------------------启动状态----------------------------------     
  72.                head_begin   = 3'b001,
  73.                head_bit     = 3'b010,
  74.                head_end     = 3'b100,                
  75. //---------------------------------停止状态----------------------------------
  76.                stop_begin   = 3'b001,
  77.                stop_bit     = 3'b010,
  78.                stop_end     = 3'b100;
  79.                
  80. parameter       YES             = 1,
  81.                 NO              = 0;                
  82. //-------------产生串行时钟,为输入时钟的二分频-------------------
  83. always @(negedge CLK)    
  84.  if(RESET)
  85.     SCL <= 0;
  86.  else      
  87.    SCL <= ~SCL; 
  88. //-----------------------------主状态程序----------------------------------
  89. always @ (posedge CLK)
  90.  if(RESET)
  91.    begin     
  92.     link_read  <= NO;
  93.     link_write <= NO;
  94.     link_head  <= NO;
  95.     link_stop  <= NO;
  96.     link_sda   <= NO;
  97.     ACK        <= 0;
  98.     RF         <= 0;
  99.     WF         <= 0;
  100.     FF         <= 0;
  101.     main_state <= Idle; 
  102.    end
  103.  else
  104.   begin   
  105.     casex(main_state)
  106.            Idle:
  107.                 begin
  108.                  link_read  <= NO;
  109.                  link_write <= NO;
  110.                  link_head  <= NO;
  111.                  link_stop  <= NO;
  112.                  link_sda   <= NO;
  113.                  if(WR)
  114.                    begin
  115.                      WF <= 1;
  116.                      main_state <= Ready ;
  117.                     end
  118.                  else if(RD)
  119.                    begin
  120.                      RF <= 1;
  121.                      main_state <= Ready ;
  122.                     end
  123.                 else
  124.                   begin
  125.                    WF <= 0;
  126.                    RF <= 0; 
  127.                    main_state <= Idle;
  128.                   end
  129.                 end 
  130.         Ready:  
  131.              begin
  132.               link_read        <= NO; 
  133.             link_write       <= NO;
  134.                link_stop        <= NO;
  135.                link_head        <= YES;
  136.                link_sda         <= YES;    
  137.                  head_buf[1:0]    <= 2'b10; 
  138.                stop_buf[1:0]    <= 2'b01; 
  139.                head_state       <= head_begin;
  140.                  FF               <= 0; 
  141.                  ACK              <= 0;  
  142.                  main_state       <= Write_start;
  143.                end
  144.  Write_start: 
  145.                if(FF == 0)
  146.                   shift_head;   
  147.               else 
  148.                 begin
  149.                   sh8out_buf[7:0]  <= {1'b1,1'b0,1'b1,1'b0,ADDR[10:8],1'b0};
  150.                   link_head        <= NO;
  151.                   link_write       <= YES;
  152.                   FF               <= 0;   
  153.                   sh8out_state     <= sh8out_bit6;
  154.                   main_state       <= Ctrl_write;
  155.                 end
  156.  Ctrl_write: 
  157.                if(FF ==0)
  158.                   shift8_out;
  159.             else
  160.               begin
  161.                    sh8out_state    <= sh8out_bit7;
  162.                   sh8out_buf[7:0] <= ADDR[7:0];
  163.                   FF              <= 0;
  164.                   main_state      <= Addr_write;
  165.                end            
  166.  Addr_write: 
  167.                if(FF == 0)
  168.                 shift8_out;
  169.               else 
  170.                 begin
  171.                   FF <= 0; 
  172.                   if(WF)
  173.                     begin  
  174.                       sh8out_state    <= sh8out_bit7;                    
  175.                       sh8out_buf[7:0] <= DATA;
  176.                       main_state      <= Data_write;
  177.                     end
  178.                   if(RF)
  179.                     begin
  180.                       head_buf        <= 2'b10;
  181.                       head_state       <= head_begin;
  182.                       main_state      <= Read_start; 
  183.                     end
  184.                 end     
  185.  Data_write:  
  186.                 if(FF == 0)
  187.                  shift8_out;
  188.             else  
  189.                 begin 
  190.                  stop_state       <= stop_begin;
  191.                  main_state       <= Stop;
  192.                  link_write       <= NO;
  193.                  FF               <= 0;
  194.                 end
  195.  Read_start:  
  196.                if(FF == 0)
  197.                    shift_head;
  198.                 else 
  199.                 begin 
  200.                   sh8out_buf       <= {1'b1,1'b0,1'b1,1'b0,ADDR[10:8],1'b1};
  201.                   link_head        <= NO;
  202.                            link_sda         <= YES;
  203.                   link_write       <= YES;
  204.                   FF               <= 0;
  205.                   sh8out_state     <= sh8out_bit6;
  206.                   main_state       <= Ctrl_read;
  207.               end
  208.  Ctrl_read: 
  209.                if(FF == 0) 
  210.                   shift8_out; 
  211.              else  
  212.                 begin 
  213.                   link_sda         <= NO;
  214.                   link_write       <= NO;
  215.                   FF               <= 0;
  216.                   sh8in_state      <= sh8in_begin;
  217.                   main_state       <= Data_read;
  218.                   end
  219.  Data_read: 
  220.               if(FF == 0) 
  221.                  shift8in;
  222.             else 
  223.                begin 
  224.                   link_stop        <= YES;
  225.                   link_sda         <= YES;
  226.                   stop_state       <= stop_bit;
  227.                   FF               <= 0;
  228.                   main_state       <= Stop;
  229.                end  
  230.  Stop: 
  231.               if(FF == 0)
  232.                shift_stop;
  233.             else 
  234.                begin
  235.                    ACK        <= 1; 
  236.                    FF         <= 0;
  237.                  main_state <= Ackn;
  238.                 end    
  239.  Ackn:   
  240.               begin
  241.                ACK         <= 0;
  242.                WF          <= 0;
  243.                RF          <= 0; 
  244.                main_state  <= Idle;
  245.              end   
  246.  default:    main_state <= Idle;
  247.  endcase
  248. end
  249. //------------------------串行数据转换为并行数据任务----------------------------------
  250. task shift8in; 
  251.  begin 
  252.   casex(sh8in_state)
  253.    sh8in_begin:
  254.        sh8in_state <= sh8in_bit7;
  255.    sh8in_bit7: if(SCL)   
  256.                  begin 
  257.                   data_from_rm[7] <= SDA; 
  258.                   sh8in_state     <= sh8in_bit6;
  259.                  end
  260.                else     
  261.                  sh8in_state <= sh8in_bit7;                   
  262.    sh8in_bit6: if(SCL) 
  263.                  begin 
  264.                    data_from_rm[6] <= SDA;
  265.                    sh8in_state     <= sh8in_bit5;
  266.                  end
  267.                else  
  268.                  sh8in_state <= sh8in_bit6;                 
  269.    sh8in_bit5: if(SCL) 
  270.                  begin 
  271.                    data_from_rm[5] <= SDA;
  272.                    sh8in_state     <= sh8in_bit4;
  273.                  end
  274.               else   
  275.                 sh8in_state <= sh8in_bit5;              
  276.    sh8in_bit4: if(SCL) 
  277.                  begin 
  278.                    data_from_rm[4] <= SDA;
  279.                    sh8in_state     <= sh8in_bit3;
  280.                  end
  281.               else   
  282.                  sh8in_state <= sh8in_bit4;                   
  283.    sh8in_bit3: if(SCL) 
  284.                  begin 
  285.                    data_from_rm[3] <= SDA;
  286.                    sh8in_state     <= sh8in_bit2;
  287.                  end
  288.               else   
  289.                 sh8in_state <= sh8in_bit3;     
  290.    sh8in_bit2: if(SCL) 
  291.                  begin 
  292.                    data_from_rm[2] <= SDA;
  293.                    sh8in_state     <= sh8in_bit1;
  294.                  end
  295.                else   
  296.                  sh8in_state <= sh8in_bit2;  
  297.    sh8in_bit1: if(SCL) 
  298.                  begin 
  299.                    data_from_rm[1] <= SDA;
  300.                    sh8in_state     <= sh8in_bit0;
  301.                  end
  302.               else   
  303.                 sh8in_state <= sh8in_bit1;                   
  304.    sh8in_bit0: if(SCL) 
  305.                  begin 
  306.                    data_from_rm[0] <= SDA;
  307.                    sh8in_state     <= sh8in_end;
  308.                  end
  309.               else   
  310.                  sh8in_state <= sh8in_bit0;
  311.     sh8in_end: if(SCL)
  312.                  begin 
  313.                    link_read   <= YES;
  314.                    FF          <=  1;                    
  315.                    sh8in_state <= sh8in_bit7; 
  316.                  end 
  317.               else   
  318.                  sh8in_state  <= sh8in_end;
  319.      default: begin
  320.       link_read    <= NO;
  321.       sh8in_state  <= sh8in_bit7;
  322.     end
  323. endcase  
  324. end  
  325. endtask
  326. //------------------------------ 并行数据转换为串行数据任务 ---------------------------
  327. task shift8_out;
  328. begin
  329.  casex(sh8out_state)
  330.        sh8out_bit7:  
  331.                 if(!SCL)
  332.                   begin 
  333.                         link_sda     <= YES;
  334.                         link_write   <= YES;
  335.                         sh8out_state <= sh8out_bit6;
  336.                       end   
  337.                     else   
  338.                         sh8out_state <= sh8out_bit7;            
  339.         sh8out_bit6: 
  340.                 if(!SCL) 
  341.                   begin 
  342.                         link_sda      <= YES;
  343.                         link_write    <= YES;
  344.                         sh8out_state  <= sh8out_bit5; 
  345.                         sh8out_buf    <= sh8out_buf<<1;
  346.                   end   
  347.               else   
  348.                         sh8out_state <= sh8out_bit6;          
  349.         sh8out_bit5: 
  350.                 if(!SCL) 
  351.                   begin 
  352.                       sh8out_state <= sh8out_bit4; 
  353.                       sh8out_buf   <= sh8out_buf<<1;
  354.                   end   
  355.                     else  
  356.                       sh8out_state <= sh8out_bit5;   
  357.         sh8out_bit4: 
  358.                 if(!SCL) 
  359.                   begin 
  360.                       sh8out_state <= sh8out_bit3;
  361.                       sh8out_buf   <= sh8out_buf<<1;
  362.                   end    
  363.                 else   
  364.                       sh8out_state <= sh8out_bit4; 
  365.         sh8out_bit3: 
  366.                 if(!SCL) 
  367.                   begin 
  368.                       sh8out_state <= sh8out_bit2; 
  369.                       sh8out_buf   <= sh8out_buf<<1; 
  370.                   end    
  371.                 else   
  372.                       sh8out_state <= sh8out_bit3;
  373.        sh8out_bit2: 
  374.                if(!SCL) 
  375.                  begin 
  376.                       sh8out_state <= sh8out_bit1; 
  377.                       sh8out_buf   <= sh8out_buf<<1;  
  378.                  end    
  379.                else   
  380.                       sh8out_state <= sh8out_bit2; 
  381.        sh8out_bit1: 
  382.                if(!SCL) 
  383.                  begin 
  384.                       sh8out_state <= sh8out_bit0; 
  385.                       sh8out_buf   <= sh8out_buf<<1; 
  386.                  end    
  387.                else   
  388.                       sh8out_state <= sh8out_bit1;  
  389.        sh8out_bit0: 
  390.                if(!SCL) 
  391.                  begin 
  392.                       sh8out_state <= sh8out_end; 
  393.                       sh8out_buf   <= sh8out_buf<<1; 
  394.                  end    
  395.                 else  
  396.                       sh8out_state <= sh8out_bit0;
  397.        sh8out_end: 
  398.                if(!SCL) 
  399.                  begin 
  400.                       link_sda         <= NO;
  401.                       link_write       <= NO; 
  402.                       FF               <= 1;
  403.                  end    
  404.                else     
  405.                       sh8out_state <= sh8out_end;     
  406.     endcase     
  407.  end 
  408. endtask
  409. //---------------------------  输出启动信号任务  ---------------------------------
  410. task shift_head;
  411. begin
  412.  casex(head_state)
  413.        head_begin: 
  414.                   if(!SCL)
  415.                     begin 
  416.                    link_write   <= NO;
  417.                        link_sda     <= YES;
  418.                        link_head    <= YES;
  419.                          head_state   <= head_bit;
  420.                     end
  421.                  else  
  422.                       head_state <= head_begin;
  423.         head_bit:   
  424.                   if(SCL)
  425.                    begin
  426.                          FF          <= 1; 
  427.                        head_buf     <= head_buf<<1;
  428.                          head_state    <= head_end;
  429.                  end    
  430.                  else
  431.                          head_state <= head_bit;
  432.         head_end: 
  433.                  if(!SCL)
  434.                    begin
  435.                          link_head    <= NO;
  436.                       link_write   <= YES;
  437.                    end    
  438.                  else
  439.                       head_state <= head_end;
  440.        endcase
  441. end
  442. endtask
  443. //---------------------------  输出停止信号任务  --------------------------------------  
  444. task shift_stop;
  445. begin
  446.  casex(stop_state)
  447.     stop_begin:  if(!SCL)
  448.                   begin 
  449.                      link_sda      <= YES;
  450.                   link_write   <= NO;
  451.                         link_stop     <= YES;
  452.                   stop_state    <= stop_bit;
  453.                end
  454.                 else   
  455.                      stop_state <= stop_begin;    
  456.    stop_bit:    if(SCL)
  457.                 begin 
  458.                       stop_buf   <= stop_buf<<1;
  459.                           stop_state <= stop_end;    
  460.             end
  461.                   else  
  462.                          stop_state<= stop_bit;
  463.    stop_end:   if(!SCL)
  464.                  begin 
  465.                            link_head  <= NO;
  466.                   link_stop  <= NO;
  467.                   link_sda   <= NO;
  468.                   FF         <= 1;
  469.                end
  470.                else  
  471.                       stop_state  <= stop_end;
  472.    endcase
  473.  end
  474. endtask
  475. endmodule

程序最终通过Synplify器的综合,并在Actel 3200DX 系列的FPGA上实现布局布线,通过布线后仿真 。

3 ) EEPROM的信号源模块和顶层模块

完成串行EEPROM读写器件的设计后,我们还需要做的重要一步是EEPROM读写器件的仿真。仿真可以分为前仿真和后仿真,前仿真是Verilog HDL的功能仿真,后仿真是Verilog HDL 代码经过综合、布局布线后的时序仿真。为此,我们还要编写了用于EEPROM读写器件的仿真测试的信号源程序。这个信号源能产生相应的读信号、写信号、并行地址信号、并行数据信号,并能接收串行EEPROM读写器件的应答信号 (ACK),来调节发送或接收数据的速度。在这个程序中,我们为了保证串行EEPROM读写器件的正确性,可以进行完整的测试,写操作时输入的地址信号和数据信号的数据通过系统命令readmemh和$fopen等系统命令读者可以参考Verilog HDL的语法部分。最后我们把信号源、EEPROM和EEPROM读写器用顶层模块连接在一起。在下面的程序就是这个信号源的Verilog HDL模型和顶层模块。

信号源模型:

  1. `timescale 1ns/1ns
  2. `define timeslice 200
  3. module Signal(RESET,CLK,RD,WR,ADDR,ACK,DATA); 
  4. output RESET;        //复位信号
  5. output CLK;          //时钟信号
  6. output RD,WR;        //读写信号
  7. output[10:0] ADDR;    //11位地址信号
  8. input ACK;           //读写周期的应答信号
  9. inout[7:0] DATA;      //数据线
  10. reg RESET;
  11. reg CLK;
  12. reg RD,WR;
  13. reg W_R;            //低位:写操作;高位:读操作 
  14. reg[10:0] ADDR;  
  15. reg[7:0]  data_to_eeprom;
  16. reg[10:0] addr_mem[0:255];
  17. reg[7:0]  data_mem[0:255];
  18. reg[7:0]  ROM[1:2048]; 
  19. integer i,j;
  20. integer OUTFILE;
  21. assign DATA = (W_R) ?  8'bz : data_to_eeprom ;
  22. //------------------------------------时钟信号输入------------------------------
  23. always #(`timeslice/2)
  24.    CLK = ~CLK; 
  25. //----------------------------------- 读写信号输入------------------------------
  26. initial 
  27.    begin
  28.      RESET = 1;
  29.      i   = 0; 
  30.      j   =0;
  31.      W_R = 0;
  32.      CLK = 0;       
  33.      RD  = 0;
  34.      WR  = 0;
  35.      #1000 ;
  36.      RESET = 0; 
  37. repeat(15)  //连续写15次数据
  38.       begin 
  39.         #(5*`timeslice);
  40.      WR = 1; 
  41.      #(`timeslice);
  42.      WR = 0;
  43.     @ (posedge ACK);
  44.      end
  45.     #(10*`timeslice);
  46.     W_R = 1;   //开始读操作
  47.     repeat(15)  //连续读15次数据 
  48.       begin
  49.       #(5*`timeslice);
  50.       RD = 1;
  51.        #(`timeslice);
  52.       RD = 0;
  53.        @ (posedge ACK);
  54.       end
  55.    end                 
  56. //-----------------------------------------写操作-----------------------------
  57. initial 
  58.   begin
  59.     $display("writing-----writing-----writing-----writing");
  60.     # (2*`timeslice);
  61.     for(i=0;i<=15;i=i+1)
  62.       begin
  63.        ADDR = addr_mem[i];                 
  64.        data_to_eeprom = data_mem[i];    
  65.        $fdisplay(OUTFILE,"@%0h  %0h",ADDR, data_to_eeprom);
  66.        @(posedge ACK) ;            
  67.      end
  68.  end   
  69. //----------------------------------------读操作----------------------------
  70. initial
  71.   @(posedge W_R)
  72.    begin
  73.     ADDR = addr_mem[0];
  74.     $fclose(OUTFILE);
  75.     $readmemh("./eeprom.dat",ROM); 
  76.     $display("Begin READING-----READING-----READING-----READING");
  77.      for(j = 0; j <= 15; j = j+1)         
  78.      begin
  79.         ADDR = addr_mem[j];          
  80.         @(posedge ACK);
  81.         if(DATA == ROM[ADDR])
  82.           $display("DATA %0h == ROM[%0h]---READ RIGHT",DATA,ADDR);
  83.         else
  84.           $display("DATA %0h != ROM[%0h]---READ WRONG",DATA,ADDR);     
  85.      end
  86.   end   
  87. initial
  88.   begin
  89.    OUTFILE = $fopen("./eeprom.dat");
  90.    $readmemh("./addr.dat",addr_mem); //地址数据存入地址存储器
  91.    $readmemh("./data.dat",data_mem); //写入EEPROM的数据存入数据存储器
  92. end
  93. endmodule
  94.   顶层模块:
  95.  `include “./Signal.v”
  96.  `include “./EEPROM.v”
  97.  `include “./EEPROM_WR.v” 
  98.  `timescale 1ns/1ns
  99.   module Top;
  100.   wire RESET;
  101.   wire CLK;
  102.   wire RD,WR;
  103.   wire ACK;
  104.   wire[10:0] ADDR;
  105.   wire[7:0]  DATA; 
  106.   wire SCL;
  107.   wire SDA;
  108.   Signal         signal(.RESET(RESET),.CLK(CLK),.RD(RD),
  109.                      .WR(WR),.ADDR(ADDR),.ACK(ACK),.DATA(DATA));
  110.   EEPROM_WR  eeprom_wr(.RESET(RESET),.SDA(SDA),.SCL(SCL),.ACK(ACK),
  111.                          .CLK(CLK),.WR(WR),.RD(RD),.ADDR(ADDR),.DATA(DATA));
  112.   EEPROM      eeprom(.sda(SDA),.scl(SCL));  
  113.  
  114. endmodule

通过前后仿真可以验证程序的正确性。这里给出的是EEPROM读写时序的前仿真波形。后仿真波形除SCL和SDA与CLK有些延迟外,信号的逻辑关系与前仿真一致:

EEPROM 的写时序

EEPROM 的读时序

说明:

以上编程、仿真、综合在PC WINDOWS NT 4.0操作系统、Synplify 、Actel Designer 、Altera Maxplus9.3及ModelSim Verilog环境下通过前后仿真,也在Unix Cadence Verilog-XL上通过前、后仿真(可综合到各种FPGA和ASIC工艺)。

推荐阅读

【Vivado那些事】如何查找官网例程及如何使用官网例程

【Vivado使用误区与进阶】总结篇

【Vivado那些事】Vivado中常用的快捷键(二)其他常用快捷键

SystemVerilog数字系统设计_夏宇闻 PDF

Verilog 里面,always,assign和always@(*)区别

图书推荐|ARM Cortex-M0 全可编程SoC原理及实现

简谈:如何学习FPGA

1202年了,还在使用虚拟机吗?Win10安装Ubuntu子系统及图形化界面详细教程

谈谈Xilinx FPGA设计的实现过程

Github 上有哪些优秀的 VHDL/Verilog/FPGA 项目

AD936x+ZYNQ搭建收音机(一)

AD936x+ZYNQ搭建OpenWIFI

Verilog 版本:Verilog-95、Verilog-2001与System Verilog区别简谈

FPGA时钟设计方案

无招胜有招-Vivado非工程模式下的详细设计

追寻ARM的起源-Acorn电脑简史及FPGA实现

面试中经常会遇到的FPGA基本概念,你会几个?

Xilinx FPGA MIPI 接口简单说明

介绍一些新手入门FPGA的优秀网站

Vivado ML(机器学习) 2021尝鲜

推荐一些可以获取免费的国外的原版书籍(电子版)网站

【Vivado那些事】FPGA的配置方式

FPGA 的重构

浅析FPGA局部动态可重构技术

ISP(图像信号处理)算法概述、工作原理、架构、处理流程

国产CPU概括

浅谈PCI Express体系结构(一)

浅谈PCI Express体系结构(二)

从电子游戏历史看IC发展的助推剂

80年代电子游戏及电脑游戏的发展历史

PCIe总线的基础知识

万字长文带你回顾电子游戏的七十多年历史(完整版)

FPGA中异步复位,同步释放的理解
 

点击上方字体即可跳转阅读

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/603048
推荐阅读
相关标签
  

闽ICP备14008679号