网站公告列表

  没有公告

加入收藏
设为首页
联系站长
您现在的位置: 超前科技开发网 >> 文章中心 >> 可编程逻辑器 >> FPGA >> 文章正文
  [推荐]使用状态机做时钟产生电路           ★★★ 【字体:
你知道么?使用状态机做时钟产生电路-独特却又最为精准(CPU设计中常用方法)
作者:佚名    文章来源:本站原创    点击数:    更新时间:2006-9-22    


介绍一款时钟发生器--独特却又最为精准(CPU设计中常用方法)
时钟发生器 clkgen 利用外来时钟信号clk 来生成一系列时钟信号clk1、fetch、alu_clk 送往CPU的其他部件。其中fetch是外来时钟 clk 的八分频信号。利用fetch的上升沿来触发CPU控制器开始执行一条指令,同时fetch信号还将控制地址多路器输出指令地址和数据地址。clk1信号用作指令寄存器、累加器、状态控制器的时钟信号。alu_clk 则用于触发算术逻辑运算单元。
module clk_gen (clk,reset,clk1,clk2,clk4,fetch,alu_clk);
input clk,reset;
output clk1,clk2,clk4,fetch,alu_clk;
wire clk,reset;
reg clk2,clk4,fetch,alu_clk;
reg[7:0] state;
parameter    S1 = 8'b00000001,
             S2 = 8'b00000010,
             S3 = 8'b00000100,
             S4 = 8'b00001000,
             S5 = 8'b00010000,
             S6 = 8'b00100000,
             S7 = 8'b01000000,
             S8 = 8'b10000000,
           idle = 8'b00000000;
                 
assign clk1 = ~clk;

always @(negedge clk)
   if(reset)
           begin
                clk2 <= 0;
                clk4 <= 1;
                fetch  <= 0;
                alu_clk <= 0;
                state <= idle;
           end
   else
           begin
              case(state)
                 S1:
                      begin
                            clk2 <= ~clk2;
                         alu_clk <= ~alu_clk;               
                           state <= S2;
                      end
                 S2:
                      begin
                          clk2 <= ~clk2;
                          clk4 <= ~clk4;
                       alu_clk <= ~alu_clk;
                         state <= S3;
                      end
                 S3:
                      begin   
                          clk2 <= ~clk2;
                         state <= S4;
                      end
                 S4:
                      begin
                          clk2  <= ~clk2;
                          clk4  <= ~clk4;
                          fetch <= ~fetch;
                          state <= S5;
                      end
                 S5:
                       begin
                          clk2  <= ~clk2;
                          state <= S6;
                       end
                 S6:
                       begin
                           clk2  <= ~clk2;
                            clk4 <= ~clk4;
                           state <= S7;
                        end
                 S7:
                        begin
                             clk2  <= ~clk2;
                             state <=  S8;
                        end
                 S8:
                        begin
                             clk2 <= ~clk2;
                            clk4  <= ~clk4;
                            fetch <= ~fetch;
                            state <= S1;
                        end
                idle:       state <= S1;   
                default:    state <= idle;               
            endcase
     end
endmodule
//--------------------------------------------------------------------------------

由于在时钟发生器的设计中采用了同步状态机的设计方法,不但使clk_gen模块的源程序可以被各种综合器综合,也使得由其生成的clk1、clk2、clk4、fetch、alu_clk 在跳变时间同步性能上有明显的提高,为整个系统的性能提高打下了良好的基础。
诸位.这样的时钟发生器无论在时序上还是功能上都是完美的,难怪一直在CPU设计中采用.

相关回复

skycanny (2005-10-11 22:43:32)
很好
任何时序电路从理论上都可以用状态机来实现
caesar000 (2005-10-12 08:15:58)
其实这个状态机就是一个移位寄存器。这个电路好的原因不在于采用了状态机,而是移位寄存器。以为寄存器的D触发器之间没有组合逻辑,这样就保证了基本脉冲序列稳定性高,而且可以实现很高的频率,性能非常好。
楼主如果能把这个状态机时序逻辑和组合逻辑分开,每个时钟产生分别由单独的一个进程产生,就更好了,脉络清晰,可读性强。
建议:
1、时序逻辑和组合逻辑分开写;
2、一个进程只描述一件事情(除了关系非常密切的)。
zqadam (2005-10-12 09:14:00)
caesar000 思路非常好,建议也贴切.不过有点问题请教您:
1:就本时钟产生器而言,我个人认为无法象您说的那样时序逻辑和组合逻辑分开写.如何分开,可否看看您斧正后的代码.
2:caesar000 老兄您说的进程是VHDL中的语句,我写的verilog代码.您的意思是不是用一个always描述一件事情?这样的话本代码的精髓和优势就失去了.望请教.
最后,感谢您的指教.望诸位本着共同学习,共同进步的原则多发好贴.
谢谢大家.
numberone (2005-10-12 09:29:21)
这是个时钟发生器电路,对时钟进行分频,必然是时序电路,何来组合逻辑?还请赐教啊

等我用DC,综合之后 在研究研究!欢迎大家一起讨论
JAYsy (2005-10-12 12:04:19)
从波形来看只有alu-clk不是2的倍数分频,也就是说这个状态机的其他三个输出都可以由简单的FF串行代替,好象没什么意义吧???
zqadam (2005-10-12 12:11:29)
当然用FF也可以解决问题,而且是最简单和基础的方法.但是JAYsy 老兄有没有理解状态机解决时钟发生器的真正优势和精髓呢?
呵呵~
cationebox (2005-10-13 07:52:02)
状态机解决时钟发生器的真正优势和精髓呢
请赐教
kwanyin (2005-10-13 09:04:47)
可能要到具体的情况下才能看到作用
zqadam (2005-10-13 10:04:26)
它相对于FF的优势应该是同步性能有明显提高啊.
要知道CPU中时钟的发生必须比其他部件的同步性更加精准.而FF则会有相对(FSM)大的时延.我们不能只停留在理论的同步上,在实际部件的精度是有区别的.
bigcrop (2005-10-13 16:36:34)
用synplify综合了一下,除了状态机用的几个逻辑门之外没发现组合逻辑啊
caesar000 (2005-10-14 02:28:02)
呵呵,不好意思,给大家造成麻烦了,状态翻转是没有组合逻辑,我的帖子里说这种方式好的理由之一就是没有组合逻辑。但是,大部分的状态机都是有组合逻辑来控制状态跳转的,看下面代码中的注释就更清楚点了。
在我改的代码中没有用reset信号,只能适合于对不同时钟间相位不作严格要求的系统中,如果要求,加上复位即可。但是那样,在系统中这个复位信号就要特别注意,不要轻易复位。
module clk_gen (clk,reset,clk1,clk2,clk4,fetch,alu_clk);

input clk,reset;
output clk1,clk2,clk4,fetch,alu_clk;

wire clk,reset;
reg clk2,clk4,fetch,alu_clk;
reg[7:0] state,next_state;

parameter    S1 = 8'b00000001,
             S2 = 8'b00000010,
             S3 = 8'b00000100,
             S4 = 8'b00001000,
             S5 = 8'b00010000,
             S6 = 8'b00100000,
             S7 = 8'b01000000,
             S8 = 8'b10000000,
           idle = 8'b00000000;

assign clk1 = ~clk;

//----------------------状态机-----------------
//状态机的时序逻辑
always @(negedge clk)
    state <= next_state;

//状态机的组合逻辑(可能没有实际的组合电路),仅表示状态跳转,
//增强代码的可读性
//既然是时钟发生器,最好不要用reset,否则复位将导致时钟中断,
//特别时钟要输出给其它模块或其它游器件用的时候
always @(state)
    case(state)
        S1 : next_state = S2;
        S2 : next_state = S3;
        S3 : next_state = S4;
        S4 : next_state = S5;
        S5 : next_state = S6;
        S6 : next_state = S7;
        S7 : next_state = S8;
        S8 : next_state = S1;
        idle : next_state = S1;
        default : next_state = idle;
    endcase

//-----------------处理逻辑-------------------------
always @(negedge clk)
    clk2 <= ~clk2;

always @(negedge clk)
    if ((state == S1) |(state == S2))
        alu_clk <= ~alu_clk;

always @(negedge clk)
    if ((state == S2) |(state == S4) | (state == S6) |(state == S8))
        clk4 <= ~clk4;

always @(negedge clk)
    if ((state == S4) |(state == S8))
        fetch <= ~fetch;

endmodule
numberone (2005-10-14 12:50:03)
楼上兄弟 思想很好,把时序逻辑和组合逻辑分开处理。我加上复位信号功能后,做了仿真,基本正确!只是加上复位信号后,源代码需要修改一下,就是always @(negedge clk)
    clk2 <= ~clk2;
修改为:
always @(negedge clk)
if(reset)
       clk2 <= 0;
    else
       clk2 <= ~clk2;
还要增加下面代码:always @(negedge clk )

if (reset)
    begin   
         clk2 <= 0;
         clk4 <= 1;
         fetch  <= 0;
         alu_clk <= 0;
         state <= idle;
       end
else
    state <= next_state;  

谢谢楼上同仁  帮我开阔了视野!!!
zqadam (2005-10-14 12:55:52)
caesar000 老兄,可以推测,您的基础是非常扎实的,经验很丰富.
在下领教了.多谢多谢.
不过您的代码还比较粗糙,不能仿真,在下改了一下,仿真后逻辑正确.
代码为:
`timescale 1ns/1ns
module clk_gen (clk,reset,clk1,clk2,clk4,fetch,alu_clk);

input clk,reset;
output clk1,clk2,clk4,fetch,alu_clk;

wire clk,reset;
reg clk2,clk4,fetch,alu_clk;
reg[7:0] state,next_state;

parameter    S1 = 8'b00000001,
             S2 = 8'b00000010,
             S3 = 8'b00000100,
             S4 = 8'b00001000,
             S5 = 8'b00010000,
             S6 = 8'b00100000,
             S7 = 8'b01000000,
             S8 = 8'b10000000,
           idle = 8'b00000000;

assign clk1 = ~clk;

//----------------------???-----------------
//????????
always @(negedge clk)
        if(reset)
           begin
                clk4 <= 1;
                fetch  <= 0;
                alu_clk <= 0;
                state <= idle;
           end
   else
    state <= next_state;

//??????????????????????????????
//????????
//??????????????reset?????????????
//??????????????????????
always @(state)
    case(state)
        S1 : next_state = S2;
        S2 : next_state = S3;
        S3 : next_state = S4;
        S4 : next_state = S5;
        S5 : next_state = S6;
        S6 : next_state = S7;
        S7 : next_state = S8;
        S8 : next_state = S1;
        idle : next_state = S1;
        default : next_state = idle;
    endcase

//-----------------????-------------------------
always @(negedge clk)
  if(reset)
     clk2 <= 0;
  else
    clk2 <= ~clk2;

always @(negedge clk)
    if ((state == S1) |(state == S2))
        alu_clk <= ~alu_clk;

always @(negedge clk)
    if ((state == S2) |(state == S4) | (state == S6) |(state == S8))
        clk4 <= ~clk4;

always @(negedge clk)
    if ((state == S4) |(state == S8))
        fetch <= ~fetch;

endmodule
zqadam (2005-10-14 13:06:53)
看了caesar000 回的帖子,感觉非常激动.
小弟我向您学到很多东西.可否与caesar000大哥交个朋友,同是天涯IC人,相逢何必曾相识.
我的QQ:29282635
MSN:zqadam@sina.com
caesar000 (2005-10-15 06:18:40)
zqadam你为了仿真加的那些代码没有实际作用,叫做冗余代码,即代码中不可有与功能无关的代码。
虽然在仿真的时候要尽可能不改动被测试对象,但是对于这种特殊情况,需要在仿真的时候改一下,且改动尽可能的小。
如果你没有用到复位的话,不能私自加复位来置初试值,而应该增加initial来初始化。另外在testbench中,也要尽可能的避免用reset来初始化,同样用initial。
对于这个代码,如果对不同时钟相位没有要求,就把复位信号去掉。如果要加就全部加上。
(我的经验很多都来自我的导师以及他的严格要求,在此向他表示衷心的感谢和深深的敬意。学到的东西我也不敢藏私,拿出来和大家分享)
qqiuda (2005-10-15 09:49:58)
楼上的兄弟果然很有经验,基本功很扎实,在下要多多学习了,呵呵

时许逻辑和组合逻辑分开写的思想很值得学习,当你有了这个思路后,可以说你在写代码的时候,头脑里浮现出来的真的是电路图,而不是写C语言时那种流程式的东西。

这个论坛人气越来越旺了,高手越来越多了,太好了,呵呵,大家多多交流阿
hexhex (2005-10-15 22:10:12)


QUOTE:
原帖由 caesar000 于 2005-10-15 06:18 发表
zqadam你为了仿真加的那些代码没有实际作用,叫做冗余代码,即代码中不可有与功能无关的代码。
虽然在仿真的时候要尽可能不改动被测试对象,但是对于这种特殊情况,需要在仿真的时候改一下,且改动尽可能的小。
...
有一个地方不明白: 这个地方的复位仅是为了仿真用吗? 不加这个复位实际电路能跑起来吗?

就是用生成的寄存器之类的初始状态是什么样的呢?
caesar000 (2005-10-15 23:17:48)
芯片上电时各个寄存器的初值是可以在布局布线或综合工具里设的,具体在哪儿我因为从来不用就忘了。
如果寄存器在上电时没有复位,又没有在软件中设置初值,那么上电时的初始状态是随机的,所以各个时钟的初始相位是随机的。
上面的代码中,zqadam加上复位是为了在仿真的时候有初值,否则初始状态是不定态,一直传递下去就没法仿真了。
我不加复位的原因是,因为是时钟产生模块,产生的时钟可能不仅仅是给自己用的,可能还会给别的模块或板上的其它器件,有时候你会想复位逻辑,但是又不想复位其它模块或器件,这时候如果把时钟也复位了,那么其它模块和器件也连带遭殃了。
cation (2005-10-16 10:41:09)
时许逻辑和组合逻辑分开写的思想很值得学习,当你有了这个思路后,可以说你在写代码的时候,头脑里浮现出来的真的是电路图,而不是写C语言时那种流程式的东西。

自己平时是作软件的
现在真的是很苦恼的
总是写出c语言那样流程的东西
苦闷
怎么去改变呢
谢谢
zqadam (2005-10-17 09:48:53)
cation 老兄,解决硬件设计与与软件设计思路不同的问题应该从以下几点考虑:
1,软件由于无法影射成器件而具有更加灵活的编程方式,可以达到随心所遇,所见及所得的效果.但是硬件设计 就不同了,虽然也是用语言编写,但是他的语句必须影射成具体的电子元件.因此,无法象软件一样随心所遇 的编写.作为一个硬件工程师,必须要懂得元器件的工作原理和方式,就这一点上是硬件工程师和软件工程师 的根本区别.
2,硬件工程师需要更多的注意时序和可综合.否则,设计出来的东西将仅仅是能通过前仿真而无法综合的垃  圾.同时由于工艺的要求,要考虑功耗等问题,.
 增加你的硬件知识和时序概念是解决问题的重要途径.
欢迎进入超前MCU技术论坛对 你知道么?使用状态机做时钟产生电路-独特却又最为精准(CPU设计中常用方法)进行讨论!

文章录入:armopen    责任编辑:armopen 
  • 上一篇文章:

  • 下一篇文章: 没有了
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    最新热点 最新推荐 相关文章
    没有相关文章
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    Copyright 2003-2006 www.mcu123.com© All Rights Reserved
    版权所有 © 超前科技开发网
    粤ICP备05005262号