所谓时序逻辑,简而言之,就是CLK驱动,不来时钟不干活,同时能自我保持。
最简单的例子,跑马灯
model led_led(input rst, input clk, output out0, output out1, output out2, output out3);
reg ary[3:0];
assign out0 = ary[0];
assign out1 = ary[1];
assign out2 = ary[2];
assign out3 = ary[3];
always @(clk)
begin
if(rst)
ary <= 4'h00;
else
begin
ary[3] <= ary[2];
ary[2] <= ary[1];
ary[1] <= ary[0];
ary[0] <= ary[3];
end
end
endmodele
有人会说那个ary的“中间变量”是不是可以省掉,还真省不了,因为它不仅仅是“临时变量”。
它是个锁存器,寄存器-----------数据保持是它的特点。而wire信号是无法保持的。
当然那4个信号单独赋值,挺形象,但是不够紧凑,C语言可以用ary = ary<<1来表示,那Verilog就如下:
model led_led(input rst, input clk, output out[3:0]);
reg ary[3:0];
assign out = ary;
always @(clk)
begin
if(rst)
ary <= 4'h00;
else
ary <= {ary[2:0],ary[3]};
end
跑马灯,用组合逻辑做不出来,是因为它的输出不仅仅跟输入有关,也跟前一个状态有关。
看到时序逻辑,很多人发现了无比强大的clk信号,到处都有他的影子,他才是真正的到处留种的风流才子。
“输出不仅仅跟输入有关,也跟前一个状态有关”,这句话,应该很熟悉吧。 -----没错,就是状态机。
后面有个专门贴讲FSM,这是一个非常有效的解决问题的工具------智商99的人可以做到智商120的设计。
如果天才用了,那肯定要更加newbility了。
下面看个十进制的计数器,是前几天给一个初学者的sample,但愿这小哥真的去看懂了,而不是搞完毕设就完蛋鸟。
module bcd_counter(rst, clk, qout);
input rst;
input clk;
output[7:0] qout;
reg [3:0] low;
reg [3:0] high;
assign qout ={high,low};
always @(posdage clk)
if(rst)
begin
low <= 4'h0;
high <= 4'h0;
end
else
begin
case(low)
0,1,2,3,4,5,6,7,8:
low <= low+4'h1;
9:
begin
low <= 4'h0;
case(high)
0,1,2,3,4,5,6,7,8:
high <= high+4'h1;
9:
high <= 0;
endcase
end
endcase
end
end module
这里面有2个十进制数据,分别占用一个nibble---4个bit,其实就是BCD码。
使用了case语句来完成,而且两个case还套起来用了,跟C语言一个样,照猫画虎即可。
细心的文艺青年,应该发现了,module的定义貌似跟以前不一样了。
不用貌似,就是不一样了,这是2种风格,其实苦B的C语言也有这个风格的。
bool fun1(X,Y)
int X, Y;
{
}
C语言的古老风格,现在很少人用了,毕竟多敲键盘,不代表多干活。
那时候C语言还没有国际标准,此K&R C是以2个工程师的名字首字母命名的。
后来才有ANSI C和ISO C(C89/C90/C99),这巨人的肩膀好高啊,搞技术就不能有恐高症。
Verilog亦是如此,建议使用输入输出定义在括号里面的风格,至少要少码好几个字母不是。
不知道有没有人注意到,上面几个帖子用的都是always @(posdage clk),
敏感信号列表中不见rst的身影
下面的判断rst的信号,决定复位还是干活,这叫做同步复位。
同步复位,你要理解成rst一有效就复位就错了。
同步的含义,与CLK同步,类似于与裆中央保持一致。
异步复位,是立即的。异步复位,同步释放。
这个话题就比较远了,后面有个帖子,会专门说这个话题。后面的会稍微有些难度,但我会为了入门的学习台阶,尽量把难的赶跑,容易的留下-----“男的赶跑,女的留下”。