chop147的个人空间 https://blog.eetop.cn/1468735 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

【案例】断电重加载时钟工程

已有 680 次阅读| 2022-7-6 06:35 |个人分类:书籍文章分享|系统分类:芯片设计| FPGA

案例编号:00000066

1.1 总体设计


1.1.1 概述

在微机的发展初期,BIOS都存放在ROM(只读存储器)中。ROM内部的资料是在ROM的制造工序中,在工厂里用特殊的方法烧录进去的,其中的内容只能读不能改,一旦烧录进去,用户只能验证写入的资料是否正确,不能再做任何修改。如果发现资料有任何错误,则只有舍弃不用,重新订做一份。ROM是在生产线上生产的,由于成本高,一般只用在大批量应用的场合。


由于ROM制造和升级的不便,后来人们发明了PROM(Programmable ROM,可编程ROM)。最初从工厂中制作完成的PROM内部并没有资料,用户可以用专用的编程器将自己的资料写入,但是这种机会只有一次,一旦写入后也无法修改,若是出了错误,已写入的芯片只能报废。PROM的特性和ROM相同,但是其成本比ROM高,而且写入资料的速度比ROM的量产速度要慢,一般只适用于少量需求的场合或是ROM量产前的验证。


EPROM(Erasable Programmable ROM,可擦除可编程ROM)芯片可重复擦除和写入,解决了PROM芯片只能写入一次的弊端。EPROM芯片有一个很明显的特征,在其正面的陶瓷封装上,开有一个玻璃窗口,透过该窗口,可以看到其内部的集成电路,紫外线透过该孔照射内部芯片就可以擦除其内的数据,完成芯片擦除的操作要用到EPROM擦除器。EPROM内资料的写入要用专用的编程器,并且往芯片中写内容时必须要加一定的编程电压(VPP=12~24V,随不同的芯片型号而定)。EPROM的型号是以27开头的,如27C020(8*256K)是一片2M Bits容量的EPROM芯片。EPROM芯片在写入资料后,还要以不透光的贴纸或胶布把窗口封住,以免受到周围的紫外线照射而使资料受损。


由于EPROM操作的不便,后来出的主板上BIOS ROM芯片大部分都采用EEPROM(Electrically Erasable Programmable ROM,电可擦除可编程ROM)。EEPROM的擦除不需要借助于其它设备,它是以电子信号来修改其内容的,而且是以Byte为最小修改单位,不必将资料全部洗掉才能写入,彻底摆脱了EPROM Eraser和编程器的束缚。EEPROM在写入数据时,仍要利用一定的编程电压,此时,只需用厂商提供的专用刷新程序就可以轻而易举地改写内容,所以,它属于双电压芯片。借助于EEPROM芯片的双电压特性,可以使BIOS具有良好的防毒功能,在升级时,把跳线开关打至“on”的位置,即给芯片加上相应的编程电压,就可以方便地升级;平时使用时,则把跳线开关打至“off”的位置,防止CIH类的病毒对BIOS芯片的非法修改。所以,仍有不少主板采用EEPROM作为BIOS芯片并作为自己主板的一大特色。


1.1.2 设计目标

整个工程由FPGA、矩阵键盘/按键、数码管和AT93C46组成,实现一个上电后能重新加载,接着上次计数的数字时钟,详细功能如下。


1、  数码管显示时钟值,共使用了6个数码管,分别表示时十位、时个位、分十位、分个位、秒十位和秒个位。


2、  矩阵键盘或者按键可以对数字时钟进行时分秒的设置。


A、  上电后,时钟默认处于计时状态,当按键1按下,跳到时间设置状态,当按键1再次按下,回到计时状态。


B、  当处于时间设置状态时,默认此刻设置的是秒个位,当按键2按下,此刻设置秒十位,以此类推,一次设置为分个位、分十位、时个位和时十位。再按下按键2,则重新设置秒个位。


C、  当处于时间设置状态时,按下按键3,则设置位的值加1,如果溢出,则变成0。例如当目前小时显示05时,设置时十位,按下按键3,变成15,再按下按键3,则变成05.当目前小时显示为03时,设置时十位,按一下按键3,变成13,再按一下按键3,则变成23,再按则为03。


3、  AT93C46则用于保存时钟值,其具有断电保护功能,断电数据不丢失。


A、  AT93C46一共可以保存128字节的数据。工程将AT93C46分成空间1和空间2。空间1占用的地址为0~3,空间2占用的地址为4~7。


B、  每隔1秒,保存当前时钟值。第一次保存到空间1,第二次保存到空间2,第三次保存带空间1,依此类推。(如果只有一个空间,则可能出现写数据过程中断电,从而得不到完整数据情况)


C、  支持8位的CRC,生成多项式

,初始值为全1。


D、  每次保存的值,时十位、时个位、分十位、分个位、秒十位和秒个位各占4bit,共3个字节,加上1个字节的CRC,一共4个字节。


E、  上电后,FPGA将读取两个空间的数值,并作CRC检验。如果两组数据的CRC检验均失败,则不重新加载;如果有一组数据CRC检验失败,则加载正确的一组数据;如果两组数据CRC检验均正确,则加载数值较大的一组数据。


1.1.3 系统结构框图

系统结构框图如下所示:


图一

1.1.4模块功能


键盘(按键)扫描模块实现功能

1、将外来异步信号打两拍处理,将异步信号同步化。

2、实现20ms按键消抖功能。

3、实现矩阵键盘或者普通案件的检测功能,并输出有效按键信号。


时钟数据产生模块实现功能

负责产生数字时钟需要的时钟信息。包括:

1、 按数字时钟方式进行计数

2、 设置数字时钟的值

3、 将时钟数据输出给外部模块使用

4、 从数据处理模块得到时钟数据,并重新加载


数码管显示模块实现功能

1、  对时钟数据进行译码,然后发到数码管显示

2、  逐一显示时分秒的值


数据处理模块实现功能

负责写到AT93C46的数据,或者从AT93C46读到数据后的处理,包括:

1、  上电后,发送EWEN命令,打开AT93C46的写保护。

2、  发送EWEN命令后,开始读存储在AT93C46的两组时钟数据;对数据进行检验,然后选择适合的数据给时钟数据产生模块加载

3、  每隔1秒从时钟数据产生模块获取时分秒的值,并产生CRC值,最后写道AT93C46上


CRC处理模块实现功能

负责CRC算法的模块,在数据处理模块内部使用


AT93C46模块实现功能

根据上游模块的EWEN、WRITE和READ命令,产生AT93C46的相应时序,从而写数据或者读到数据。至于数据是什么、有什么用,不关心,只关心AT93C46的时序。


1.1.5顶层信号

  

信号名

  

接口方向

定义

clk

输入

系统时钟,50Mhz

rst_n

输入

低电平复位信号

Key_col

输入

4位矩阵键盘列信号,默认高电平,开发板按键为普通按键时,不需要该信号

Key_row

输出

4位矩阵键盘行信号,默认低电平,开发板按键为普通按键时,不需要该信号

Key

输入

3位按键信号,开发板按键为矩阵键盘时,不需要该信号

Segment

输出

8位数码管段选信号

Seg_sel

输出

6位数码管位选信号

Mo

输出

At93c46数据输出

Mi

输入

At93c46数据输入

Cs

输出

At93c46片选信号

Sk

输出

At93c46时钟信号


1.1.6参考代码

  1. `define KEY_SCAN

  2. module at93c46_top_scan(

  3.     clk      ,

  4.     rst_n    ,

  5.     key_col  ,

  6.     mo       ,

  7.     cs       ,

  8.     mi       ,

  9.     sk       ,

  10.     key_row  ,

  11.     seg_sel  ,

  12.     seg_data

  13.     );


  14.     parameter    TIME_1S = 50_000_000;



  15.     input               clk          ;

  16.     input               rst_n        ;

  17.     input [3:0]         key_col      ;

  18.     input               mo           ;


  19.     output              cs           ;

  20.     output              mi           ;

  21.     output              sk           ;

  22.     output[3:0]         key_row      ;

  23.     output[5:0]         seg_sel      ;

  24.     output[7:0]         seg_data     ;


  25.     wire                rdy          ;

  26.     wire                rdata_vld    ;

  27.     wire  [3:0]         key_en       ;


  28.     wire  [23:0]        data_load    ;

  29.     wire                data_load_vld;


  30.     wire  [23:0]        clk_data_out ;


  31.     wire  [6:0]         addr         ;

  32.     wire  [1:0]         mode         ;

  33.     wire                start        ;


  34.     wire  [7:0]         wdata        ;

  35.     wire  [7:0]         rdata        ;





  36.              `ifdef KEY_SCAN

  37.     key_scan  u_key_scan(

  38.                 .clk     (clk    ),

  39.                 .rst_n   (rst_n  ),

  40.                 .key_col (key_col),

  41.                 .key_row (key_row),

  42.                 .key_en  (key_en )

  43.              );

  44.             `else            

  45.    key_module  u_key_module(

  46.                 .clk     (clk     ),

  47.                 .rst_n   (rst_n   ),

  48.                 .key_in  (~key_col),

  49.                 .key_vld (key_en  )

  50.              );


  51.             `endif

  52.     clock_data#(.TIME_1S(TIME_1S)) u_clock_data(

  53.                 .clk           (clk           ),

  54.                 .rst_n         (rst_n         ),

  55.                 .data_load     (data_load     ),

  56.                 .data_load_vld (data_load_vld ),

  57.                 .key_en        (key_en        ),

  58.                 .data_out      (clk_data_out  )

  59.     );


  60.     seg_disp#(.SEG_NUM(6)) u_seg_disp(

  61.                 .rst_n       (rst_n       ),

  62.                 .clk         (clk         ),

  63.                 .din         (clk_data_out),

  64.                 .din_vld     ({6{1'b1}}   ),

  65.                 .seg_sel     (seg_sel     ),

  66.                 .segment     (seg_data    )

  67.              );

  68.    

  69.    

  70.    data_processor#(.TIME_1S(TIME_1S)) u_data_pro(

  71.                       .clk        (clk         )  ,

  72.                       .rst_n      (rst_n       )  ,

  73.                       .din        (clk_data_out)  ,

  74.                       .start      (start       )  ,

  75.                       .mode       (mode        )  ,

  76.                       .addr       (addr        )  ,

  77.                       .wdata      (wdata       )  ,

  78.                       .rdata      (rdata       )  ,

  79.                       .rdata_vld  (rdata_vld   )  ,

  80.                       .rdy        (rdy         )  ,

  81.                       .dout       (data_load   )  ,

  82.                       .dout_vld   (data_load_vld )   

  83.    

  84.     );



  85.     at93c46_mix u_at93c46_mix(

  86.                    .clk        (clk        )  ,

  87.                    .rst_n      (rst_n      )  ,

  88.                    .start      (start      )  ,

  89.                    .mode       (mode       )  ,

  90.                    .addr       (addr       )  ,

  91.                    .wdata      (wdata      )  ,

  92.                    .rdata      (rdata      )  ,

  93.                    .rdata_vld  (rdata_vld  )  ,

  94.                    .rdy        (rdy        )  ,

  95.                    .do         (mo         )  ,

  96.                    .di         (mi         )  ,

  97.                    .cs         (cs         )  ,

  98.                    .sk         (sk         )   

  99.     );



  100. endmodule

复制代码

本工程会应用于不同的开发板,主要区别在于使用普通按键还是矩阵键盘,顶层代码中针对这一点进行了设计,如何开发板使用的是矩阵键盘,则顶层代码不需要改,如果使用的是普通按键,只需要将顶层代码最上面的一行删除或者注释掉就可以了。





1.2 键盘(按键)扫描模块设计1.2.1接口信号

下面为使用矩阵键盘时的接口信号:

  

信号

  

接口方向

定义

clk

输入

系统时钟

rst_n

输入

低电平复位信号

key_col

输入

矩阵键盘列输入信号

Key_row

输出

矩阵键盘行输出信号

Key_en

输出

按键按下位置指示信号

下面是使用普通按键时的接口信号:

  

信号

  

接口方向

定义

clk

输入

系统时钟

rst_n

输入

低电平复位信号

Key_in

输入

按键输入信号

Key_vld

输出

按键按下指示信号



1.2.2 设计思路

在前面的按键控制数字时钟的案例中已经有介绍,所以这里不在过多介绍,详细介绍请看下方链接:

http://fpgabbs.com/forum.php?mod=viewthread&tid=310

1.2.3参考代码

  1. module  key_scan(

  2.                  clk    ,

  3.                  rst_n  ,

  4.                  key_col,

  5.                  key_row,

  6.                  key_en   

  7.                );



  8.     parameter      KEY_W    =   4      ;

  9.     parameter      CHK_COL  =   0      ;

  10.     parameter      CHK_ROW  =   1      ;

  11.     parameter      DELAY    =   2      ;

  12.     parameter      WAIT_END =   3      ;

  13.     parameter      COL_CNT  =   16     ;

  14.     parameter      TIME_20MS=   1000000;


  15.     input               clk              ;

  16.     input               rst_n            ;

  17.     input [3:0]         key_col          ;


  18.     output[3:0]         key_en           ;

  19.     output[KEY_W-1:0]   key_row          ;


  20.     reg   [3:0]         key_out          ;

  21.     reg   [KEY_W-1:0]   key_row          ;

  22.     reg                 key_vld          ;



  23.     reg   [3:0]         key_col_ff0      ;

  24.     reg   [3:0]         key_col_ff1      ;

  25.     reg   [1:0]         key_col_get      ;

  26.     reg   [3:0]         key_en           ;

  27.     wire                end_shake_cnt    ;

  28.     reg                 end_shake_cnt_ff0;

  29.     reg   [3:0]         state_c          ;

  30.     reg   [19:0]        shake_cnt        ;

  31.     reg   [3:0]         state_n          ;

  32.     reg   [1:0]         row_index        ;

  33.     reg   [15:0]        row_cnt          ;

  34.     wire                col2row_start    ;

  35.     wire                row2del_start    ;

  36.     wire                del2wait_start   ;

  37.     wire                wait2col_start   ;

  38.     wire                add_row_cnt      ;

  39.     wire                end_row_cnt      ;

  40.     wire                add_shake_cnt    ;

  41.     wire                add_row_index    ;

  42.     wire                end_row_index    ;



  43. always  @(posedge clk or negedge rst_n)begin

  44.     if(rst_n==1'b0)begin

  45.         key_col_ff0 <= 4'b1111;

  46.         key_col_ff1 <= 4'b1111;

  47.     end

  48.     else begin

  49.         key_col_ff0 <= key_col    ;

  50.         key_col_ff1 <= key_col_ff0;

  51.     end

  52. end



  53. always @(posedge clk or negedge rst_n) begin

  54.     if (rst_n==0) begin

  55.         shake_cnt <= 0;

  56.     end

  57.     else if(add_shake_cnt) begin

  58.         if(end_shake_cnt)

  59.             shake_cnt <= 0;

  60.         else

  61.             shake_cnt <= shake_cnt+1 ;

  62.    end

  63. end

  64. assign add_shake_cnt = key_col_ff1!=4'hf;

  65. assign end_shake_cnt = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;



  66. always  @(posedge clk or negedge rst_n)begin

  67.     if(rst_n==1'b0)begin

  68.         state_c <= CHK_COL;

  69.     end

  70.     else begin

  71.         state_c <= state_n;

  72.     end

  73. end


  74. always  @(*)begin

  75.     case(state_c)

  76.         CHK_COL: begin

  77.                      if(col2row_start )begin

  78.                          state_n = CHK_ROW;

  79.                      end

  80.                      else begin

  81.                          state_n = CHK_COL;

  82.                      end

  83.                  end

  84.         CHK_ROW: begin

  85.                      if(row2del_start)begin

  86.                          state_n = DELAY;

  87.                      end

  88.                      else begin

  89.                          state_n = CHK_ROW;

  90.                      end

  91.                  end

  92.         DELAY :  begin

  93.                      if(del2wait_start)begin

  94.                          state_n = WAIT_END;

  95.                      end

  96.                      else begin

  97.                          state_n = DELAY;

  98.                      end

  99.                  end

  100.         WAIT_END: begin

  101.                      if(wait2col_start)begin

  102.                          state_n = CHK_COL;

  103.                      end

  104.                      else begin

  105.                          state_n = WAIT_END;

  106.                      end

  107.                   end

  108.        default: state_n = CHK_COL;

  109.     endcase

  110. end

  111. assign col2row_start = state_c==CHK_COL  && end_shake_cnt;

  112. assign row2del_start = state_c==CHK_ROW  && row_index==3 && end_row_cnt;

  113. assign del2wait_start= state_c==DELAY    && end_row_cnt;

  114. assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;


  115. always  @(posedge clk or negedge rst_n)begin

  116.     if(rst_n==1'b0)begin

  117.         key_row <= 4'b0;

  118.     end

  119.     else if(state_c==CHK_ROW)begin

  120.         key_row <= ~(1'b1 << row_index);

  121.     end

  122.     else begin

  123.         key_row <= 4'b0;

  124.     end

  125. end






  126. always @(posedge clk or negedge rst_n) begin

  127.     if (rst_n==0) begin

  128.         row_index <= 0;

  129.     end

  130.     else if(add_row_index) begin

  131.         if(end_row_index)

  132.             row_index <= 0;

  133.         else

  134.             row_index <= row_index+1 ;

  135.    end

  136.    else if(state_c!=CHK_ROW)begin

  137.        row_index <= 0;

  138.    end

  139. end

  140. assign add_row_index = state_c==CHK_ROW && end_row_cnt;

  141. assign end_row_index = add_row_index  && row_index == 4-1 ;



  142. always @(posedge clk or negedge rst_n) begin

  143.     if (rst_n==0) begin

  144.         row_cnt <= 0;

  145.     end

  146.     else if(add_row_cnt) begin

  147.         if(end_row_cnt)

  148.             row_cnt <= 0;

  149.         else

  150.             row_cnt <= row_cnt+1 ;

  151.    end

  152. end

  153. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;

  154. assign end_row_cnt = add_row_cnt  && row_cnt == 16-1 ;




  155. always  @(posedge clk or negedge rst_n)begin

  156.     if(rst_n==1'b0)begin

  157.         key_col_get <= 0;

  158.     end

  159.     else if(state_c==CHK_COL && end_shake_cnt ) begin

  160.         if(key_col_ff1==4'b1110)

  161.             key_col_get <= 0;

  162.         else if(key_col_ff1==4'b1101)

  163.             key_col_get <= 1;

  164.         else if(key_col_ff1==4'b1011)

  165.             key_col_get <= 2;

  166.         else

  167.             key_col_get <= 3;

  168.     end

  169. end



  170. always  @(posedge clk or negedge rst_n)begin

  171.     if(rst_n==1'b0)begin

  172.         key_out <= 0;

  173.     end

  174.     else if(state_c==CHK_ROW && end_row_cnt)begin

  175.         key_out <= {row_index,key_col_get};

  176.     end

  177.     else begin

  178.         key_out <= 0;

  179.     end

  180. end


  181. always  @(posedge clk or negedge rst_n)begin

  182.     if(rst_n==1'b0)begin

  183.         key_vld <= 1'b0;

  184.     end

  185.     else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin

  186.         key_vld <= 1'b1;

  187.     end

  188.     else begin

  189.         key_vld <= 1'b0;

  190.     end

  191. end



  192. always  @(*)begin

  193.     if(rst_n==1'b0)begin

  194.         key_en = 0;

  195.     end

  196.     else if(key_vld && key_out==0)begin

  197.         key_en = 4'b0001;

  198.     end

  199.     else if(key_vld && key_out==1)begin

  200.         key_en = 4'b0010;

  201.     end

  202.     else if(key_vld && key_out==2)begin

  203.         key_en = 4'b0100;

  204.     end

  205.     else begin

  206.         key_en = 0;

  207.     end

  208. end



复制代码


1.3 时间数据产生模块设计
1.3.1接口信号

  

信号

  

接口方向

定义

clk

输入

系统时钟

rst_n

输入

低电平复位信号

Data_load

输入

重载的时钟数据,每四个一组,共6组。由高位至低位,分别是时十位、时个位、分十位、分个位、秒十位和秒个位

Data_load_vld

输入

重载的时钟数据有效指示信号,当有效时,采用data_load的数据为最新的时钟数据

Key_num

输入

按键位置输入信号,key_vld有效时,该信号有效

Key_vld

输入

按键值有效信号,为1时表示检测到一个按键

data_out

输出

当前的时钟数据,每四个1组,共六组,由高位至低位,分别是时十位、时个位、分十位、分个位、秒十位和秒个位



1.3.2设计思路

本模块相对于前面的按键控制数字时钟案例中的时间数据产生模块来说,总体的设计思路是相同的,只是增加了一个重载的时钟信号,对于此信号的设计也比较简单,只需要在时分秒的个位和十位计数器中增加一句:在重载的时钟数据有效的时候,使计数器输出重载的时钟对应的数据即可,比如秒个位计数器应该输出重载时钟数据的第0到第3位数据,秒十位计数器应该输出重载时钟数据的第4到第7位数据,以此类推。


其他详细的设计思路可以看一下往期按键控制数字时钟的文章:

1.3.3参考代码



  1. module clock_data(

  2.     clk          ,

  3.     rst_n        ,

  4.     data_load    ,

  5.     data_load_vld,

  6.     key_en       ,

  7.     data_out   

  8.     );


  9.     parameter      TIME_1S    =       50_000_000   ;


  10.     input               clk          ;

  11.     input               rst_n        ;

  12.     input               data_load_vld;

  13.     input [23:0]        data_load    ;

  14.     input [ 3:0]        key_en       ;

  15.     output[23:0]        data_out     ;

  16.     wire  [23:0]        data_out     ;



  17.     reg   [25:0]        cnt_1s       ;

  18.     reg   [3:0]         miao_ge      ;

  19.     reg   [3:0]         miao_shi     ;

  20.     reg   [3:0]         fen_ge       ;

  21.     reg   [3:0]         fen_shi      ;

  22.     reg   [3:0]         shi_ge       ;

  23.     reg   [3:0]         shi_shi      ;

  24.     reg   [2:0]         set_sel      ;

  25.     reg                 set_flag     ;

  26.     wire                add_set_sel  ;

  27.     wire                add_cnt_1s   ;

  28.     wire                add_miao_ge  ;

  29.     wire                add_miao_shi ;

  30.     wire                add_fen_ge   ;

  31.     wire                add_fen_shi  ;

  32.     wire                add_shi_ge   ;

  33.     wire                add_shi_shi  ;

  34.     wire                end_cnt_1s   ;

  35.     wire                end_set_sel  ;

  36.     wire                end_miao_ge  ;

  37.     wire                end_miao_shi ;

  38.     wire                end_fen_ge   ;

  39.     wire                end_fen_shi  ;

  40.     wire                end_shi_ge   ;

  41.     wire                end_shi_shi  ;

  42.     wire                set_miao_ge  ;

  43.     wire                set_miao_shi ;

  44.     wire                set_fen_ge   ;

  45.     wire                set_fen_shi  ;

  46.     wire                set_shi_ge   ;

  47.     wire                set_shi_shi  ;


  48.     reg  [ 3:0]         x            ;

  49.     reg  [ 2:0]         y            ;


  50.     always  @(posedge clk or negedge rst_n)begin

  51.         if(rst_n==1'b0)begin

  52.             set_flag <= 1'b0;

  53.         end

  54.         else if(key_en[0]) begin

  55.             set_flag <= ~ set_flag;

  56.         end

  57.     end




  58.     always @(posedge clk or negedge rst_n)begin

  59.         if(!rst_n)begin

  60.             set_sel <= 0;

  61.         end

  62.         else if(add_set_sel)begin

  63.             if(end_set_sel)

  64.                 set_sel <= 0;

  65.             else

  66.                 set_sel <= set_sel + 1;

  67.         end

  68.         else if(set_flag==0)begin

  69.             set_sel <= 0;

  70.         end

  71.     end


  72.     assign add_set_sel = set_flag && key_en[1];      

  73.     assign end_set_sel = add_set_sel && set_sel==6-1 ;   


  74.     always @(posedge clk or negedge rst_n)begin

  75.         if(!rst_n)begin

  76.             cnt_1s <= 0;

  77.         end

  78.         else if(add_cnt_1s)begin

  79.             if(end_cnt_1s)

  80.                 cnt_1s <= 0;

  81.             else

  82.                 cnt_1s <= cnt_1s + 1;

  83.         end

  84.     end


  85.     assign add_cnt_1s = set_flag==0 ;      

  86.     assign end_cnt_1s = add_cnt_1s && cnt_1s==TIME_1S-1 ;   






  87.     always @(posedge clk or negedge rst_n)begin

  88.         if(!rst_n)begin

  89.             miao_ge <= 0;

  90.         end

  91.         else if(add_miao_ge)begin

  92.             if(end_miao_ge)

  93.                 miao_ge <= 0;

  94.             else

  95.                 miao_ge <= miao_ge + 1;

  96.         end

  97.         else if(data_load_vld)begin

  98.             miao_ge <=  data_load[3:0];

  99.         end

  100.     end


  101.     assign add_miao_ge = (end_cnt_1s || set_miao_ge) ;      

  102.     assign end_miao_ge = add_miao_ge && miao_ge==10-1 ;

  103.     assign set_miao_ge = set_flag    && set_sel==0 && key_en[2];   




  104.     always @(posedge clk or negedge rst_n)begin

  105.         if(!rst_n)begin

  106.             miao_shi <= 0;

  107.         end

  108.         else if(add_miao_shi)begin

  109.             if(end_miao_shi)

  110.                 miao_shi <= 0;

  111.             else

  112.                 miao_shi <= miao_shi + 1;

  113.         end

  114.         else if(data_load_vld)begin

  115.             miao_shi <= data_load[7:4];

  116.         end

  117.     end


  118.     assign add_miao_shi = (end_miao_ge || set_miao_shi);      

  119.     assign end_miao_shi = add_miao_shi && miao_shi==6-1;

  120.     assign set_miao_shi = set_flag     && set_sel==1 && key_en[2];   



  121.     always @(posedge clk or negedge rst_n)begin

  122.         if(!rst_n)begin

  123.             fen_ge <= 0;

  124.         end

  125.         else if(add_fen_ge)begin

  126.             if(end_fen_ge)

  127.                 fen_ge <= 0;

  128.             else

  129.                 fen_ge <= fen_ge + 1;

  130.         end

  131.         else if(data_load_vld)begin

  132.             fen_ge <= data_load[11:8];

  133.         end

  134.     end


  135.     assign add_fen_ge = (end_miao_shi || set_fen_ge);      

  136.     assign end_fen_ge = add_fen_ge && fen_ge==10-1;

  137.     assign set_fen_ge = set_flag     && set_sel==2 && key_en[2];   



  138.     always @(posedge clk or negedge rst_n)begin

  139.         if(!rst_n)begin

  140.             fen_shi <= 0;

  141.         end

  142.         else if(add_fen_shi)begin

  143.             if(end_fen_shi)

  144.                 fen_shi <= 0;

  145.             else

  146.                 fen_shi <= fen_shi + 1;

  147.         end

  148.         else if(data_load_vld)begin

  149.             fen_shi <= data_load[15:12];

  150.         end

  151.     end


  152.     assign add_fen_shi = (end_fen_ge || set_fen_shi);      

  153.     assign end_fen_shi = add_fen_shi && fen_shi==6-1;

  154.     assign set_fen_shi = set_flag     && set_sel==3 && key_en[2];   


  155.    always @(posedge clk or negedge rst_n)begin

  156.         if(!rst_n)begin

  157.             shi_ge <= 0;

  158.         end

  159.         else if(add_shi_ge)begin

  160.             if(end_shi_ge)

  161.                 shi_ge <= 0;

  162.             else

  163.                 shi_ge <= shi_ge + 1;

  164.         end

  165.         else if(data_load_vld)begin

  166.             shi_ge <= data_load[19:16];

  167.         end

  168.     end


  169.     assign add_shi_ge = (end_fen_shi || set_shi_ge);      

  170.     assign end_shi_ge = add_shi_ge && shi_ge==x-1;

  171.     assign set_shi_ge = set_flag     && set_sel==4 && key_en[2];   



  172.     always @(posedge clk or negedge rst_n)begin

  173.         if(!rst_n)begin

  174.             shi_shi <= 0;

  175.         end

  176.         else if(add_shi_shi)begin

  177.             if(end_shi_shi)

  178.                 shi_shi <= 0;

  179.             else

  180.                 shi_shi <= shi_shi + 1;

  181.         end

  182.         else if(data_load_vld)begin

  183.             shi_shi <= data_load[23:20];

  184.         end

  185.     end


  186.     assign add_shi_shi = (end_shi_ge || set_shi_shi);      

  187.     assign end_shi_shi = add_shi_shi && shi_shi==y-1;

  188.     assign set_shi_shi = set_flag     && set_sel==5 && key_en[2];   


  189.     always  @(*)begin

  190.         if(shi_shi<2)

  191.             x =  10;

  192.         else

  193.             x =   4;

  194.     end


  195.     always  @(*)begin

  196.         if(set_flag && set_sel==5 && shi_ge>=4)

  197.             y = 2;

  198.         else

  199.             y = 3;

  200.     end

  201.     assign data_out = {shi_shi,shi_ge,fen_shi,fen_ge,miao_shi,miao_ge};



  202. endmodule


复制代码




1.4 数码管显示模块设计

1.4.1接口信号

  

信号

  

接口方向

定义

clk

输入

系统时钟,50MHz

rst_n

输入

低电平复位信号

din

输入

当前的时钟数据,每四位一组,共 6 组。由高位至低位,分

  

别是时十位、时个位、分十位、分个位、秒十位和秒个位的

  

值。

Seg_sel

输出

数码管位选信号

Segment

输出

数码管段选信号



1.4.2设计思路

数码管显示在前面的案例文章已经有讲述,这里不再进行介绍,想了解的可以看一下往期文章:


1.4.3参考代码

  1. always @(posedge clk or negedge rst_n)begin

  2.     if(!rst_n)begin

  3.         cnt_2ms <= 0;

  4.     end

  5.     else if(add_cnt_2ms)begin

  6.         if(end_cnt_2ms)

  7.             cnt_2ms <= 0;

  8.         else

  9.             cnt_2ms <= cnt_2ms + 1;

  10.     end

  11. end


  12. assign add_cnt_2ms = 1;      

  13. assign end_cnt_2ms = add_cnt_2ms && cnt_2ms==TIME_2MS-1 ;   


  14. always @(posedge clk or negedge rst_n)begin

  15.     if(!rst_n)begin

  16.         cnt_sel <= 0;

  17.     end

  18.     else if(add_cnt_sel)begin

  19.         if(end_cnt_sel)

  20.             cnt_sel <= 0;

  21.         else

  22.             cnt_sel <= cnt_sel + 1;

  23.     end

  24. end


  25. assign add_cnt_sel = end_cnt_2ms;      

  26. assign end_cnt_sel = add_cnt_sel && cnt_sel== SEG_NUM-1;   




  27. always  @(posedge clk or negedge rst_n)begin

  28.     if(rst_n==1'b0)begin

  29.         seg_sel <= {SEG_NUM{1'b1}};

  30.     end

  31.     else begin

  32.         seg_sel <= ~(1'b1 << cnt_sel);

  33.     end

  34. end


  35. always  @(posedge clk or negedge rst_n)begin

  36.     if(rst_n==1'b0)begin

  37.         din_ff0 <= 0;

  38.     end

  39.     else begin

  40.         for(ii=0;ii<SEG_NUM;ii=ii+1)begin

  41.             if(din_vld[ii]==1'b1)begin

  42.                 din_ff0[(ii+1)*4-1 -:4] <= din[(ii+1)*4-1 -:4];

  43.             end

  44.             else begin

  45.                 din_ff0[(ii+1)*4-1 -:4] <= din_ff0[(ii+1)*4-1 -:4];

  46.             end

  47.         end

  48.     end

  49. end


  50. always  @(*)begin

  51.     seg_tmp = din_ff0[(cnt_sel+1)*4-1 -:4];

  52. end



  53. always@(posedge clk or negedge rst_n)begin

  54.     if(rst_n==1'b0)begin

  55.         segment<=NUM_0;

  56.     end

  57.     else begin

  58.         case(seg_tmp)

  59.             4'd0:segment <= NUM_0;

  60.             4'd1:segment <= NUM_1;

  61.             4'd2:segment <= NUM_2;

  62.             4'd3:segment <= NUM_3;

  63.             4'd4:segment <= NUM_4;

  64.             4'd5:segment <= NUM_5;

  65.             4'd6:segment <= NUM_6;

  66.             4'd7:segment <= NUM_7;

  67.             4'd8:segment <= NUM_8;

  68.             4'd9:segment <= NUM_9;

  69.             default:begin

  70.                 segment <= NUM_ERR;

  71.             end

  72.         endcase

  73.     end

  74. end


  75. endmodul

复制代码



1.5 数据处理模块设计


1.5.1接口信号

  

信号

  

接口方向

定义

clk

输入

系统时钟

rst_n

输入

低电平复位信号

din

输入

时钟数据,每四位1组,共 6 组。由高位至低位,分别是时十位、时个位、分十位、分个位、秒十位和秒个位的值

dout

输出

用于加载的时钟数据,每四位1组,共 6 组。由高位至低位,分别是时十位、时个位、分十位、分个位、秒十位和秒个位的值

dout_vld

输出

时钟加载有效指示信号,高电平有效

Start

输出

对AT93C46进行打开写保护、写数据或者读数据命令。注意,在rdy=0时,不应该使start有效

mode

输出

对AT93C46的操作模式,start有效时,此值有效,

  

0:打开写保护操作(EWEN)

  

1:写数据操作(WRITE)

  

2:读数据操作(READ)

  

其他:不应该出现

addr

输出

读写AT93C46的地址信号,同时亦是EWEN的指示信号,使用EWEN命令时,此值必须为7’b1100000,在start有效时,此信号有效

Wdata

输出

写到AT93C46的数据,start=1,且mode=1时,此值有效

rdy

输入

AT93C46接口模块准备好信号,只有当其为1时,才能向接口模块发送命令

rdata

输入

从AT93C46读取的数据

rdata_vld

输入

从AT93C46读到的数据有效指示信号


1.5.2设计思路

本模块主要负责写到AT93C46的数据或者读出的数据的处理,上电后,发送EWEN指令给AT93C46接口模块,打开AT93C46的写保护;发送EWEN命令后,开始读取存储在AT93C46保存的两组时钟数据;每隔1秒读取输入时钟数据的值,并产生CRC值,写到AT93C46上。

根据上面的功能描述,该模块采用状态机进行架构,可划分四个状态:打开写保护状态(S_EWEN)、读数据状态(S_READ)、空闲状态(S_IDLE)和写数据状态(S_WRIT),状态的跳转图如下:



由于功能要求只在刚上电或者复位的时候才读取AT93C46中的数据,因此刚上电就是写保护打开状态,或者复位有效时,进入写保护打开状态。由于复位是由按键控制的,因此在按下的时候会产生抖动,可能会导致产生很多个start,因此延时一段时间之后,如果AT93C46接口模块准备好,便进入读数据状态。数据读完之后,就进入空闲状态,等待计时1秒之后,开始将输入的时钟数据写入AT93C46中,写完四个字节数据之后重新回到空闲状态,等待计时1秒,如此循环。


下面介绍一下该模块中其他信号的设计思路:

时钟计数器time_cnt:该计数器是计数1秒的时间,从写保护打开状态跳转到读数据状态需要的延时和空闲状态跳转写数据状态需要的1秒的时间可使用此计数器表示;加一条件为1,表示一直计数;结束条件为数50000000个,表示1秒的时间,数完就清零。

写数据计数器wr_cnt:该计数器用于对要写入AT93C46的数据进行计数;加一条件为state_c==S_WRIT&& rdy,表示在写数据状态的时候,如果AT93C46接口模块准备好,就开始计数;结束条件为数4个,3个字节的时钟数据加上1个字节的CRC校验,共四个字节,数完就清零。

读数据计数器:该计数器数的是从AT93C46读出,并经过CRC处理的数据字节数;加一条件为state_c==S_READ&& crc_dout_vld,表示在读数据状态的时候,CRC处理完就计数一个;结束条件为数8个,AT93C46两个区域内共存有8个字节的数据,数完就清零。

写区间选择信号write_sel:初始状态为0,表示选择区间0~3,当写操作完成之后,该信号取反,变为1,表示选择区间4~7。

读写地址信号addr:初始状态为0,根据下方的表格(AT93C46的指令集),当处于写数据状态的时候,地址为7bit,由于本工程只会使用区间0~7来存储数据,因此地址为4bit0加上写区间选择信号write_sel加上写数据计数器;当处于写保护打开状态的时候,地址应为7’b11xxxxx,其中“x”表示不关注,工程中设为0即可;当处于读数据状态的时候,根据读数据计数器的变化选择地址即可,即地址为4’b0加上rd_cnt



AT93C46指令集


AT93C46开始命令start:初始状态为0,表示AT93C46不工作,当(add_wr_cnt || start_read),也就是在写数据、读数据或者写保护打开状态的时候,该信号拉高,指示AT93C46工作。


写数据wdata:该信号表示要往AT93C46中写入的数据,初始状态为0。前三个字节写入输入的时钟数据,第四个字节写入CRC。


AT93C46返回数据dout_temp:从AT93C46读出的8个字节数据,经过串并转换之后保存在该信号中。


第一区间CRC错误指示信号dout0_err:初始状态为0,表示没有错误;当读数据计数器数到第4个的时候,表示CRC模块已经完成了第一区间数据的校验,如果校验结果为0,表示正确,如果校验结果不等于0,表示输出错误。


第二区间CRC错误指示信号dout1_err:为了使错误指示信号跟数据能对齐,采用组合逻辑设计,当CRC模块输出数据为0,表示没有错误,该信号为0,如果CRC模块输出数据不为0,表示有错误,该信号为1。


用于加载的时钟数据dout:初始状态为0;当读数据计数器数完的时候,如果区间1和区间2的检验都没有错误,则比较AT93C46返回数据的高4字节和低4字节的大小,输出大的;如果区间1检验正确,区间2检验错误,则输出高4字节数据,反之则输出低4字节数据。


时钟加载有效指示信号dout_vld:初始状态为0,表示时钟数据无效;当读数据计数器数完的时候,如果第一区间和第二区间有最少一个正确,该信号就拉高,表示待加载的时钟数据有效,其他情况不拉高。


1.5.3参考代码

  1.     always  @(posedge clk or negedge rst_n)begin

  2.         if(rst_n==1'b0)begin

  3.             state_c <= S_EWEN;

  4.         end

  5.         else begin

  6.             state_c <= state_n;

  7.         end

  8.     end


  9.     always  @(*)begin

  10.         case(state_c)

  11.             S_EWEN : begin

  12.                 if(ewen2read_start)begin

  13.                     state_n = S_READ ;

  14.                 end

  15.                 else begin

  16.                     state_n = state_c;

  17.                 end

  18.             end

  19.             S_READ : begin

  20.                 if(read2idle_start)begin

  21.                     state_n = S_IDLE;

  22.                 end

  23.                 else begin

  24.                     state_n = state_c;

  25.                 end

  26.             end

  27.             S_IDLE : begin

  28.                 if(idle2write_start)begin

  29.                     state_n = S_WRITE;

  30.                 end

  31.                 else begin

  32.                     state_n = state_c;

  33.                 end

  34.             end

  35.             S_WRITE : begin

  36.                 if(write2idle_start)begin

  37.                     state_n = S_IDLE;

  38.                 end

  39.                 else begin

  40.                     state_n = state_c;

  41.                 end

  42.             end

  43.             default : begin

  44.                 state_n = S_EWEN;

  45.             end

  46.         endcase

  47.     end



  48.     assign   ewen2read_start  = state_c==S_EWEN  && add_time_cnt && time_cnt==1000-1 && rdy;

  49.     assign   read2idle_start  = state_c==S_READ  && end_rd_cnt      ;

  50.     assign   idle2write_start = state_c==S_IDLE  && end_time_cnt    ;

  51.     assign   write2idle_start = state_c==S_WRITE && end_wr_cnt      ;



  52.     always @(posedge clk or negedge rst_n)begin

  53.         if(!rst_n)begin

  54.             time_cnt <= 0;

  55.         end

  56.         else if(add_time_cnt)begin

  57.             if(end_time_cnt)

  58.                 time_cnt <= 0;

  59.             else

  60.                 time_cnt <= time_cnt + 1;

  61.         end

  62.     end


  63.     assign add_time_cnt = 1;      

  64.     assign end_time_cnt = add_time_cnt && time_cnt==TIME_1S-1 ;   



  65.     always @(posedge clk or negedge rst_n)begin

  66.         if(!rst_n)begin

  67.             wr_cnt <= 0;

  68.         end

  69.         else if(add_wr_cnt)begin

  70.             if(end_wr_cnt)

  71.                 wr_cnt <= 0;

  72.             else

  73.                 wr_cnt <= wr_cnt + 1;

  74.         end

  75.     end


  76.     assign add_wr_cnt = state_c==S_WRITE && rdy;

  77.     assign end_wr_cnt = add_wr_cnt && wr_cnt == WR_NUM-1 ;



  78.     always @(posedge clk or negedge rst_n)begin

  79.         if(!rst_n)begin

  80.             rd_cnt <= 0;

  81.         end

  82.         else if(add_rd_cnt)begin

  83.             if(end_rd_cnt)

  84.                 rd_cnt <= 0;

  85.             else

  86.                 rd_cnt <= rd_cnt + 1;

  87.         end

  88.     end


  89.     assign add_rd_cnt = state_c==S_READ && crc_dout_vld;

  90.     assign end_rd_cnt = add_rd_cnt && rd_cnt == RD_NUM-1 ;



  91.     assign start_read = state_c==S_READ && flag_wait_crc==0 && rdy;


  92.     always  @(posedge clk or negedge rst_n)begin

  93.         if(rst_n==1'b0)begin

  94.             flag_wait_crc <= 0;

  95.         end

  96.         else if(start_read)begin

  97.             flag_wait_crc <= 1;

  98.         end

  99.         else if(crc_dout_vld)begin

  100.             flag_wait_crc <= 0;

  101.         end

  102.     end


  103.     always  @(posedge clk or negedge rst_n)begin

  104.         if(rst_n==1'b0)begin

  105.             mode <= EWEN;

  106.         end

  107.         else if(state_c==S_EWEN) begin

  108.             mode <= EWEN;

  109.         end

  110.         else if(state_c==S_WRITE)begin

  111.             mode <= WRITE;

  112.         end

  113.         else if(state_c==S_READ)begin

  114.             mode <= READ;

  115.         end

  116.     end




  117.     always  @(posedge clk or negedge rst_n)begin

  118.         if(rst_n==1'b0)begin

  119.             crc_din <= 0;

  120.         end

  121.         else if(add_wr_cnt && end_wr_cnt==0) begin

  122.             crc_din <= din[8*(D_LEN-wr_cnt)-1 -:8];

  123.         end

  124.         else begin

  125.             crc_din <= rdata;

  126.         end

  127.     end


  128.     always  @(posedge clk or negedge rst_n)begin

  129.         if(rst_n==1'b0)begin

  130.             crc_din_vld <= 1'b0;

  131.         end

  132.         else if(add_wr_cnt && end_wr_cnt==0) begin

  133.             crc_din_vld <= 1'b1;

  134.         end

  135.         else begin

  136.             crc_din_vld <= rdata_vld;

  137.         end

  138.     end


  139.     always  @(posedge clk or negedge rst_n)begin

  140.         if(rst_n==1'b0)begin

  141.             crc_clr <= 1'b0;

  142.         end

  143.         else if((add_rd_cnt && rd_cnt==4-1)|| read2idle_start || write2idle_start) begin

  144.             crc_clr <= 1'b1;

  145.         end

  146.         else begin

  147.             crc_clr <= 1'b0;

  148.         end

  149.     end


  150.     always  @(posedge clk or negedge rst_n)begin

  151.         if(rst_n==1'b0)begin

  152.             addr <= 0;

  153.         end

  154.         else if(state_c==S_WRITE) begin

  155.             addr <= {4'b0,write_sel,wr_cnt[1:0]};

  156.         end

  157.         else if(state_c==S_EWEN) begin

  158.             addr <= 7'b1100000;

  159.         end

  160.         else begin

  161.             addr <= {4'b0,rd_cnt};

  162.         end

  163.     end


  164.     always  @(posedge clk or negedge rst_n)begin

  165.         if(rst_n==1'b0)begin

  166.             write_sel <= 1'b0;

  167.         end

  168.         else if(write2idle_start) begin

  169.             write_sel <= ~write_sel;

  170.         end

  171.     end


  172.   



  173.     always  @(posedge clk or negedge rst_n)begin

  174.         if(rst_n==1'b0)begin

  175.             wdata <= 0;

  176.         end

  177.         else if(wr_cnt==WR_NUM-1) begin

  178.             wdata <= crc_dout;

  179.         end

  180.         else begin

  181.             wdata <= din[8*(D_LEN-wr_cnt)-1 -:8];

  182.         end

  183.     end


  184.     always  @(posedge clk or negedge rst_n)begin

  185.         if(rst_n==1'b0)begin

  186.             start <= 1'b0;

  187.         end

  188.         else if(add_wr_cnt || start_read || ewen2read_start)begin

  189.             start <= 1'b1;

  190.         end

  191.         else begin

  192.             start <= 1'b0;

  193.         end

  194.     end


  195.     always  @(posedge clk or negedge rst_n)begin

  196.         if(rst_n==1'b0)begin

  197.             dout_temp<= 0;

  198.         end

  199.         else if(rdata_vld) begin

  200.             dout_temp[(8-rd_cnt)*8-1 -:8] <= rdata;

  201.         end

  202.     end


  203.     always  @(posedge clk or negedge rst_n)begin

  204.         if(rst_n==1'b0)begin

  205.             dout0_err <= 1'b0;

  206.         end

  207.         else if(add_rd_cnt && rd_cnt==4-1) begin

  208.             if(crc_dout!=0)

  209.                 dout0_err <= 1'b1;

  210.             else

  211.                 dout0_err <= 1'b0;

  212.         end

  213.     end


  214.     always  @(*)begin

  215.         if(crc_dout!=0)

  216.             dout1_err = 1'b1;

  217.         else

  218.             dout1_err = 1'b0;

  219.     end



  220.     assign dout0_temp = dout_temp[8*8-1 -:24];

  221.     assign dout1_temp = dout_temp[4*8-1 -:24];




  222.    

  223.     always  @(posedge clk or negedge rst_n)begin

  224.         if(rst_n==1'b0)begin

  225.             dout <= 0;

  226.         end

  227.         else if(end_rd_cnt) begin

  228.             if(dout0_err==1'b0 && dout1_err==1'b0)begin

  229.                 if(dout0_temp > dout1_temp)

  230.                     dout <= dout0_temp;

  231.                 else

  232.                     dout <= dout1_temp;

  233.             end

  234.             else if(dout0_err==1'b0)begin

  235.                 dout <= dout0_temp;

  236.             end

  237.             else begin

  238.                 dout <= dout1_temp;

  239.             end

  240.         end

  241.     end



  242.     always  @(posedge clk or negedge rst_n)begin

  243.         if(rst_n==1'b0)begin

  244.             dout_vld <= 1'b0;

  245.         end

  246.         else if(end_rd_cnt) begin

  247.             if(dout0_err && dout1_err)begin

  248.                 dout_vld <= 1'b0;

  249.             end

  250.             else begin

  251.                 dout_vld <= 1'b1;

  252.             end

  253.         end

  254.         else begin

  255.             dout_vld <= 1'b0;

  256.         end

  257.     end



  258.    crc8_d8 u_crc8_d8(

  259.               .clk      (clk          ),

  260.               .rst_n    (rst_n        ),

  261.               .clr      (crc_clr      ),

  262.               .din_vld  (crc_din_vld  ),

  263.               .din      (crc_din      ),

  264.               .dout_vld (crc_dout_vld ),

  265.               .dout     (crc_dout     )      

  266.     );


复制代码



1.6 CRC处理模块设计


1.6.1接口信号

  

信号

  

接口方向

定义

clk

输入

系统时钟

rst_n

输入

低电平复位信号

Clr

输入

清零信号,将当前的CRC运算复位,重新开始新的运算。

din_vld

输入

CRC输入数据有效信号

din

输入

CRC运算输入的数据

dout

输出

CRC运算结果

dout_vld

输出

CRC输出有效指示信号


1.6.2设计思路

该模块主要的作用是负责CRC运算,在数据处理模块内部使用,多项式为 ,本模块代码不需要设计,使用网上的生成工具(https://www.easics.com/crctool/

),输入多项式即可生成,具体设置请看下图。


关于CRC的原理和并行、串行实现的方法,有另外的视频进行讲解,这里不在进行介绍。

1.6.3参考代码

  1.     assign d   =    din    ;

  2.     assign c   =    dout   ;



  3.     always  @(posedge clk or negedge rst_n)begin

  4.         if(rst_n==1'b0)begin

  5.             dout <= 0;

  6.         end

  7.         else if(clr)begin

  8.             dout <= 0;

  9.         end

  10.         else if(din_vld) begin

  11.             dout[0] <= d[7] ^ d[6] ^ d[0] ^ c[0] ^ c[6] ^ c[7];

  12.             dout[1] <= d[6] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[6];

  13.             dout[2] <= d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[2] ^ c[6];

  14.             dout[3] <= d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[1] ^ c[2] ^ c[3] ^ c[7];

  15.             dout[4] <= d[4] ^ d[3] ^ d[2] ^ c[2] ^ c[3] ^ c[4];

  16.             dout[5] <= d[5] ^ d[4] ^ d[3] ^ c[3] ^ c[4] ^ c[5];

  17.             dout[6] <= d[6] ^ d[5] ^ d[4] ^ c[4] ^ c[5] ^ c[6];

  18.             dout[7] <= d[7] ^ d[6] ^ d[5] ^ c[5] ^ c[6] ^ c[7];

  19.         end

  20.     end


  21.     always  @(posedge clk or negedge rst_n)begin

  22.         if(rst_n==1'b0)begin

  23.             dout_vld <= 0;

  24.         end

  25.         else begin

  26.             dout_vld <= din_vld;

  27.         end

  28.     end


  29. endmodul

复制代码



1.7 AT93C46接口模块设计


1.7.1接口信号

  

信号

  

接口方向

定义

clk

输入

系统时钟

rst_n

输入

低电平复位信号

addr

输入

地址信号,在start有效时,此值有效

Wdata

输入

写数据信号,在start有效时,此值有效

start

输入

开始命令,仅在rdy=1时才有效

mode

输出

操作模式指示信号,start有效时,此值有效

rdy

输入

准备好信号。当rdy为1时,start才有效。在rdy为0时,不能使start有效

rdata

输入

从AT93C46读取的数据

rdata_vld

输出

从AT93C46读到的数据有效指示信号

do

输出

AT93C46数据输出

di

输出

AT93C46数据输入

cs

输出

AT93C46片选信号

sk

输出

AT93C46时钟,200KHz


1.7.2设计思路

参考数据手册,本模块主要实现三个命令:打开写保护(EWEN)、读数据(READ)和写数据(WRITE)。


下面时EWEN命令的时序图,结合上文提到的AT93C46的指令集,打开写保护指令的时序应该是写10bit数据之后,等待TCS时间,然后结束。


下面是READ命令的时序图。结合上文提到的AT93C46的指令集,读数据命令对应的时序应该是写10bit数据钟后,读8bit数据,等待TCS时间,然后结束。



下面是WRITE命令的时序图。结合上文提到的AT93C46的指令集,写数据命令对应的时序应该是写18bit数据,cs拉低TCS时间,等待TWP(5ms)时间,然后结束。



根据上述的时序介绍,本模块采用3个计数器的架构,下面是计数器的架构图。



架构中的三个计数器分别为时钟计数器cnt0、比特计数器cnt1和阶段计数器cnt2,flag_work为工作状态指示信号。


时钟计数器cnt0:该计数器用来计数时钟的个数。加一条件为flag_work,表示进入工作状态就开始计数。结束条件为数x个,根据不同的工作模式和所处的阶段不同而变化。包括SK的时钟周期数、等待时间TCS、等待5ms时间。


比特计数器cnt1:该计数器用来数有多少bit数据,加一条件为end_cnt0,表示每数完1bit,就加1;结束条件为数y个,y分别为10(EWEN)、18(READ和WRITE)、1(等待TCS和5ms)。


阶段计数器cnt2:该计数器用来对每个指令需要的阶段进行计数。加一条件为end_cnt1,表示发送完一组数据就加一;结束条件为数u个,u分别为2(EWEN和READ)、3(WRITE)。


除了上述的计数器之外,还有一些比较重要的信号,我们来分析一下如何进行设计。


工作状态指示信号flag_work:初始状态为0,表示处于空闲状态;当收到开始命令start的时候,变化变为1,由空闲状态转为工作状态;当前指令的时序结束之后,也就是阶段计数器cnt2数完,就变为0,进入空闲状态。


待发送数据dout:当接收到开始命令的时候,根据AT93C46的指令集,将SB、Opcode、Address和data按照顺序拼接到一起。


模式暂存器mode_reg:为保证在发送时序期间保持不变,在接收到开始命令的时候,将操作模式指示信号进行暂存。


AT93C46时钟sk:时钟频率为200KHz,工程的系统时钟为50MHz,因此sk一个完整的周期需要250个系统时钟周期,初始状态设为低电平,当时钟计数器数到125个的时候置为高电平,时钟计数器数完,在置为低电平。


AT93C46数据输入di:在每个指令时序的第一阶段,也就是cnt2=1-1的时候,根据比特计数器要数的个数,将待发送的数据dout送给di。


AT93C46片选信号cs:在写比特数据、等待5ms期间为高,其他时候都为低,所以该信号拉高的条件为(start_vld==1 || (add_cnt2 && cnt2==2-1 &&end_cnt2==0))。其他时候片选信号都为低,所以拉低的条件为((add_cnt2&& cnt2==1-1) || end_cnt2)。


读取数据rdata:在读模式下,处于第一阶段,并且在第11~18bit的时候,将AT93C46输出数据do保存到rdata里面。


准备好信号rdy:由组合逻辑产生,当接收到开始命令,或者处于工作状态的时候,为低电平,表示没有准备好;其他时刻为高电平,表示准备好。


1.7.3参考代码

  1.     assign start_vld = flag_work==0 && start;


  2.     always  @(*)begin

  3.         if(mode==EWEN)

  4.             opcode = 3'b100;

  5.         else if(mode==WRITE)

  6.             opcode = 3'b101;

  7.         else

  8.             opcode = 3'b110;

  9.     end


  10.     always  @(posedge clk or negedge rst_n)begin

  11.         if(rst_n==1'b0)begin

  12.             dout <= 0;

  13.         end

  14.         else if(start_vld) begin

  15.             dout <={opcode,addr,wdata};

  16.         end

  17.     end



  18.     always  @(posedge clk or negedge rst_n)begin

  19.         if(rst_n==1'b0)begin

  20.             mode_reg <= 0;

  21.         end

  22.         else if(start_vld) begin

  23.             mode_reg <= mode;

  24.         end

  25.     end


  26.     always  @(posedge clk or negedge rst_n)begin

  27.         if(rst_n==1'b0)begin

  28.             flag_work <= 0;

  29.         end

  30.         else if(start_vld) begin

  31.             flag_work <= 1;

  32.         end

  33.         else if(end_cnt2)begin

  34.             flag_work <= 0;

  35.         end

  36.     end



  37.    

  38.     always @(posedge clk or negedge rst_n)begin

  39.         if(!rst_n)begin

  40.             cnt0 <= 0;

  41.         end

  42.         else if(add_cnt0)begin

  43.             if(end_cnt0)

  44.                 cnt0 <= 0;

  45.             else

  46.                 cnt0 <= cnt0 + 1;

  47.         end

  48.     end


  49.     assign add_cnt0 = flag_work;      

  50.     assign end_cnt0 = add_cnt0 && cnt0== x-1;   


  51.     always @(posedge clk or negedge rst_n)begin

  52.         if(!rst_n)begin

  53.             cnt1 <= 0;

  54.         end

  55.         else if(add_cnt1)begin

  56.             if(end_cnt1)

  57.                 cnt1 <= 0;

  58.             else

  59.                 cnt1 <= cnt1 + 1;

  60.         end

  61.     end


  62.     assign add_cnt1 = end_cnt0;      

  63.     assign end_cnt1 = add_cnt1 && cnt1==y-1 ;



  64.     always @(posedge clk or negedge rst_n)begin

  65.         if(!rst_n)begin

  66.              cnt2 <= 0;

  67.          end

  68.          else if(add_cnt2)begin

  69.              if(end_cnt2)

  70.                   cnt2 <= 0;

  71.              else

  72.                   cnt2 <= cnt2 + 1;

  73.           end

  74.     end

  75.    

  76.     assign add_cnt2 = end_cnt1;      

  77.     assign end_cnt2 = add_cnt2 && cnt2==u-1;   



  78.     assign en_sk1 = add_cnt0 && cnt0==x/2-1 && cnt2==1-1;

  79.     assign en_sk0 = end_cnt0                            ;

  80.     always  @(posedge clk or negedge rst_n)begin

  81.         if(rst_n==1'b0)begin

  82.             sk <= 0;

  83.         end

  84.         else if(add_cnt0 && cnt0==x/2-1 && cnt2==1-1)begin

  85.             sk <= 1;

  86.         end

  87.         else if(end_cnt0)begin

  88.             sk <= 0;

  89.         end

  90.     end


  91.     assign en_di = add_cnt0 && cnt0==1-1 && cnt2==1-1;


  92.     always  @(posedge clk or negedge rst_n)begin

  93.         if(rst_n==1'b0)begin

  94.             di <= 0;

  95.         end

  96.         else if(en_di) begin

  97.             di <= dout[17-cnt1];

  98.         end

  99.     end



  100.     always  @(posedge clk or negedge rst_n)begin

  101.         if(rst_n==1'b0)begin

  102.             cs <= 0;

  103.         end

  104.         else if(start_vld==1 || (add_cnt2 && cnt2==2-1 && end_cnt2==0)) begin

  105.             cs <= 1;

  106.         end

  107.         else if((add_cnt2 && cnt2==1-1) || end_cnt2)begin

  108.             cs <= 0;

  109.         end

  110.     end



  111.     always  @(posedge clk or negedge rst_n)begin

  112.         if(rst_n==1'b0)begin

  113.             rdata <= 0;

  114.         end

  115.         else if(end_cnt0 && cnt1 >=10 && cnt2==1-1 && mode_reg==READ ) begin

  116.             rdata[17-cnt1] <= do;

  117.         end

  118.     end


  119.     always  @(posedge clk or negedge rst_n)begin

  120.         if(rst_n==1'b0)begin

  121.             rdata_vld <= 0;

  122.         end

  123.         else begin

  124.             rdata_vld <= end_cnt2 && mode_reg==READ;

  125.         end

  126.     end


  127.     always  @(*)begin

  128.         if(start || flag_work)

  129.             rdy = 1'b0;

  130.         else

  131.             rdy = 1'b1;

  132.     end



  133.     always  @(*)begin

  134.         if(mode_reg==WRITE && cnt2==1-1)begin

  135.             x = 250;

  136.             y =  18;

  137.             u =   3;  

  138.         end

  139.         else if(mode_reg==WRITE && (cnt2==2-1 ))begin

  140.             x = 250 ;

  141.             y =   1 ;

  142.             u =   3 ;  

  143.         end

  144.         else if(mode_reg==WRITE && cnt2==3-1)begin

  145.             x = 500000;  

  146.             y =   1 ;

  147.             u =   3 ;  

  148.         end

  149.         else if(mode_reg==READ && cnt2==1-1)begin

  150.             x = 250;

  151.             y =  18;

  152.             u =   2;  

  153.         end

  154.         else if(mode_reg==READ && cnt2==2-1)begin

  155.             x = 250 ;

  156.             y =   1 ;

  157.             u =   2 ;  

  158.         end

  159.         else if(mode_reg==EWEN && cnt2==1-1)begin

  160.             x = 250;

  161.             y =  10;

  162.             u =   2;  

  163.         end

  164.         else begin

  165.             x = 250 ;  

  166.             y =   1 ;

  167.             u =   2 ;  

  168.         end

  169.     end



  170. endmodul

复制代码



1.8 效果和总结

本工程上板之后,可通过复位来验证现象,若要通过断电来进行验证,需要将工程烧录进开发板才行。

1.8.1db603开发板

由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。


1.8.2mp801开发板

由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。


1.8.3ms980试验箱

由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。

详细的设计视频教程和工程源码请到明德扬论坛学习!

MDY最新推出《FPGA至简设计案例200例》项目,每周固定更新至少1个案例,可到明德扬论坛www.fpgabbs.cn下载学习。



温馨提示:明德扬除了培训学习还有元器件一站式配单业务,人工服务的模式采购采购客服人员联系方式:明德扬元器件一站式配单服务咨询:易老师13112063618

明德扬科技教育




点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 8

    粉丝
  • 1

    好友
  • 4

    获赞
  • 2

    评论
  • 457

    访问数
关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-11-25 08:02 , Processed in 0.032385 second(s), 8 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部