本文为明德扬原创及录用**,转载请注明出处! 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、实现矩阵键盘或者普通案件的检测功能,并输出有效按键信号。 时钟数据产生模块实现功能
数据处理模块实现功能
负责写到AT93C46的数据,或者从AT93C46读到数据后的处理,包括: 1、 上电后,发送EWEN命令,打开AT93C46的写保护。 2、 发送EWEN命令后,开始读存储在AT93C46的两组时钟数据;对数据进行检验,然后选择适合的数据给时钟数据产生模块加载 3、 每隔1秒从时钟数据产生模块获取时分秒的值,并产生CRC值,最后写道AT93C46上 CRC处理模块实现功能
负责CRC算法的模块,在数据处理模块内部使用 AT93C46模块实现功能
根据上游模块的EWEN、WRITE和READ命令,产生AT93C46的相应时序,从而写数据或者读到数据。至于数据是什么、有什么用,不关心,只关心AT93C46的时序。 1.2.2 设计思路在前面的按键控制数字时钟的案例中已经有介绍,所以这里不在过多介绍,详细介绍请看下方链接: http://fpgabbs.com/forum.html?mod=viewthread&tid=310 1.4.2设计思路数码管显示在前面的案例**已经有讲述,这里不再进行介绍,想了解的可以看一下往期**: 1.4.3参考代码
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_2ms <= 0;
- end
- else if(add_cnt_2ms)begin
- if(end_cnt_2ms)
- cnt_2ms <= 0;
- else
- cnt_2ms <= cnt_2ms + 1;
- end
- end
- assign add_cnt_2ms = 1;
- assign end_cnt_2ms = add_cnt_2ms && cnt_2ms==TIME_2MS-1 ;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- seg_sel <= {SEG_NUM{1'b1}};
- end
- else begin
- seg_sel <= ~(1'b1 << cnt_sel);
- end
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- din_ff0 <= 0;
- end
- else begin
- for(ii=0;ii<SEG_NUM;ii=ii+1)begin
- if(din_vld[ii]==1'b1)begin
- din_ff0[(ii+1)*4-1 -:4] <= din[(ii+1)*4-1 -:4];
- end
- else begin
- din_ff0[(ii+1)*4-1 -:4] <= din_ff0[(ii+1)*4-1 -:4];
- end
- end
- end
- end
- always @(*)begin
- seg_tmp = din_ff0[(cnt_sel+1)*4-1 -:4];
- end
- always@(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- segment<=NUM_0;
- end
- else begin
- case(seg_tmp)
- 4'd0:segment <= NUM_0;
- 4'd1:segment <= NUM_1;
- 4'd2:segment <= NUM_2;
- 4'd3:segment <= NUM_3;
- 4'd4:segment <= NUM_4;
- 4'd5:segment <= NUM_5;
- 4'd6:segment <= NUM_6;
- 4'd7:segment <= NUM_7;
- 4'd8:segment <= NUM_8;
- 4'd9:segment <= NUM_9;
- default:begin
- segment <= NUM_ERR;
- end
- endcase
- end
- end
- endmodul
[color=rgb(51, 102, 153) !important]复制代码
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 1.6.2设计思路
该模块主要的作用是负责CRC运算,在数据处理模块内部使用,多项式为 ,本模块代码不需要设计,使用网上的生成工具(https://www.easics.com/crctool/ ),输入多项式即可生成,具体设置请看下图。 1.6.3参考代码
- assign d = din ;
- assign c = dout ;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- dout <= 0;
- end
- else if(clr)begin
- dout <= 0;
- end
- else if(din_vld) begin
- dout[0] <= d[7] ^ d[6] ^ d[0] ^ c[0] ^ c[6] ^ c[7];
- dout[1] <= d[6] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[6];
- dout[2] <= d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[2] ^ c[6];
- dout[3] <= d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[1] ^ c[2] ^ c[3] ^ c[7];
- dout[4] <= d[4] ^ d[3] ^ d[2] ^ c[2] ^ c[3] ^ c[4];
- dout[5] <= d[5] ^ d[4] ^ d[3] ^ c[3] ^ c[4] ^ c[5];
- dout[6] <= d[6] ^ d[5] ^ d[4] ^ c[4] ^ c[5] ^ c[6];
- dout[7] <= d[7] ^ d[6] ^ d[5] ^ c[5] ^ c[6] ^ c[7];
- end
- end
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- dout_vld <= 0;
- end
- else begin
- dout_vld <= din_vld;
- end
- end
- endmodul
[color=rgb(51, 102, 153) !important]复制代码
1.7 AT93C46接口模块设计
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个的时候置为高电平,时钟计数器数完,在置为低电平。
1.8 效果和总结
本工程上板之后,可通过复位来验证现象,若要通过断电来进行验证,需要将工程烧录进开发板才行。
1.8.1db603开发板
由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
1.8.2mp801开发板
由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
1.8.3ms980试验箱
由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的**: 《至简设计系列_LCD入门案例_边框显示》 《至简设计系列_BCD译码实现》 《至简设计系列_简易计算器》 《至简设计系列_基于FPGA的超声波测距系统设计》 《至简设计系列_串口回环工程》 《至简设计系列_矩阵按键检测》 《至简设计系列_闹钟》 《至简设计系列_7段数码管显示》 《阻塞赋值与非阻塞赋值》 《参数例化时自动计算位宽的解决办法》
1.9 公司简介明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。
|