本小白从零学习fpga过程中,学习看书及刷题中遇到的笔记
注意变量后面要用逗号!!!!!!!!!“,”
wire表示直通,即输入有变化,输出马上无条件地反映(如与、非门的简单连接)。
reg表示一定要有触发,输出才会反映输入的状态。
reg相当于存储单元,wire相当于物理连线。reg表示一定要有触发,没有输入的时候可以保持原来的值,但不直接与实际的硬件电路对应。
对于一般的状态机,首先需要对各个状态进行编号,而这个编号有gray编码,独热编码,以及需要高速电路的状态输出和状态联系起来编码。然后对于整体的状态机的书写可以采用三段式,即一段采用时序电路,控制状态的变化,二段采用组合逻辑电路,控制产生下一状态的组合逻辑,三段即控制产生输出的组合逻辑
assign用wire
always用reg
assign 组合逻辑和always@(*)组合逻辑
verilog描述组合逻辑一般常用的有两种:assign赋值语句和always@(*)语句。两者之间的差别有:
1. 被assign赋值的信号定义为wire型,被always@(*)结构块下的信号定义为reg型,值得注意的是,这里的reg并不是一个真正的触发器,
只有敏感列表为上升沿触发的写法才会综合为触发器,在仿真时才具有触发器的特性。
2. 另外一个区别则是更细微的差别:举个例子,
wire a;
reg b;
assign a = 1'b0;
always@(*)
b = 1'b0;
在这种情况下,做仿真时a将会正常为0, 但是b却是不定态。这是为什么?verilog规定,always@(*)中的*是指该always块内的
所有输入信号的变化为敏感列表,也就是仿真时只有当always@(*)块内的输入信号产生变化,该块内描述的信号才会产生变化,
而像always@(*) b = 1'b0,这种写法由于1'b0一直没有变化,所以b的信号状态一直没有改变,由于b是组合逻辑输出,所以
复位时没有明确的值(不定态),而又因为always@(*)块内没有敏感信号变化,因此b的信号状态一直保持为不定态。
创建变量input [7:0] a, b, c, d;
也可以wire [7:0]vector_1,vector_2;
数据左移一位代表乘以2 数据右移一位代表除以2
输入变量用绿色
中间变量用黄色
输出变量用红色
阻塞赋值,在串行块begin-end中依次执行,组合逻辑电路使用always要用阻塞赋值
非阻塞赋值,用于寄存器型变量赋值,只能用于initial和always块中,不允许用于连续赋值assign,时序逻辑电路使用
一个always中不能既用阻塞赋值又用非阻塞赋值
一个always推荐只对一个变量进行赋值,方便后期进行修改
归约运算符可以对向量的位执行 AND、OR 和 XOR,从而产生一位输出:
& a[3:0] // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0] // OR: b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0] // XOR: c[2]^c[1]^c[0]
这些是只有一个作数的一元运算符(类似于 NOT 运算符 ! 和 ~)。你也可以反转这些的输出来创建NAND、NOR和XNOR门,例如(~&d[7:0])。
几种产生Latch的情况
1. 组合逻辑中if语句没有else;
2. 组合逻辑中case的条件不能够完全列举时且不写default;
3. 组合逻辑中输出变量赋值给自己。
区分一个设计是组合逻辑电路还是时序逻辑电路主要是看数据工作是不是在时钟沿下进行的
时序逻辑电路在时钟沿进行
//设置时间函数和系统函数
$timeformat(-9,0,"ns",6);//10^-9为一纳秒,精确到小数点后0位,单位要与-9对应;如-3为毫秒,打印的最小字符为6个
//监测系统打印函数
$monitor("@time%t:in1=%b in2=%b sel=%b out=%b",$time,in1,in2,sel,out);//打印对应时间的二进制数
脉冲标志信号 cnt_flag
for循环的使用,组合逻辑中
创建中间变量 integer i; for(i=0;i<100;i=i+1)
module top_module(
input [99:0] in,
output [99:0] out
);
integer i;
always @(*)begin
for(i=0;i<100;i=i+1)begin
out[i]=in[99-i];
end
end
endmodule
分频器 把输入信号的频率变成成倍数地低于输入频率的输出信号
对时钟分频
1、厂家提供锁相环PLL实现时钟任意的分频和倍频
2、用代码编写分频器
1.硬件消抖 少量按钮 用RS触发器
2.软件消抖 按钮较多
modelsim ctrl+G
状态机(FSM Finite State Machine)又称同步有限状态机 时序逻辑电路中非常重要
输入
输出
每个状态是什么
一般简化成最简单的
分类:共同点:状态跳转只和输入有关
Moore型状态机 :最后输出只和当前状态有关,与输入无关
Mealy型状态机 :最后输出和当前状态以及输入都有关
状态机的每一个状态代表一个事件,从当前事件到另一个事件称为状态跳转或状态转移
执行该事件,然后跳转到另一个事件
特别时候发生有先后顺序,或者有时序规律的事情
数字电路系统中,计数器、微处理器等都可以用状态机来表示
//独热码 状态在4-24
parameter IDLE=3'b001;
parameter ONE=3'b010;
parameter TWO=3'b100;
//二进制编码 低速系统中,状态小于4个
/*parameter IDLE=3'b000;
parameter ONE=3'b001;
parameter TWO=3'b010;*/
//格雷码 相邻参数只有一个bit位不同 状态大于24个
/*parameter IDLE=3'b00;
parameter ONE=3'b01;
parameter TWO=3'b11;*/
PLL搜索CLK
ROM rom_8x256 rom的ip核8位宽,256深度
波特率:码元 Bps 波特每秒
比特率: bps 比特每秒
比特率=波特率 * 单个调制状态对应的二进制位数。
如果使用的是9600的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。
casez语句,不关心z处的值,从左到又优先读
casez (in[3:0])
4'bzzz1: ...
4'bzz10: ...
4'bz100: ...
4'b1000: ...
default: ...
endcase
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*)
casez(in[7:0])
8'bzzzzzzz1:pos=3'd0;
8'bzzzzzz10:pos=3'd1;
8'bzzzzz100:pos=3'd2;
8'bzzzz1000:pos=3'd3;
8'bzzz10000:pos=3'd4;
8'bzz100000:pos=3'd5;
8'bz1000000:pos=3'd6;
8'b10000000:pos=3'd7;
default pos=3'd0;
endcase
endmodule
!!!for 中begin和end
for(i=0;i<3;i++)begin
if(in[i]==1)out=out+2'd1;
else
out=out;
end
例题
在 [99:0] 中给定一个 100 位的输入向量。我们想知道每个位与其邻居之间的一些关系:
out_both:此输出向量的每个位应指示相应的输入位及其左侧的邻居是否都为“1”。
例如,out_both[98]应该表明in[98]和in[99]是否都是 1。
由于in[99]左边没有邻居,答案很明显,所以我们不需要知道out_both[99 ] .
out_any:此输出向量的每个位应指示相应的输入位及其右侧的邻居是否为“1”。
例如,out_any[2]应该指示in[2]或in[1]是否为 1。由于in[0]右侧没有邻居,
因此答案很明显,因此我们不需要知道out_any[0 ] .
out_different:此输出向量的每个位都应指示相应的输入位是否与其左侧的邻居不同。
例如,out_diff[98]应该指示in[98]是否与in[99]不同。对于这部分,将向量视为环绕,
因此in[99]左侧的邻居是in[0]。
法1:使用for语句
module top_module(
input [99:0] in,
output [98:0] out_both,
output [99:1] out_any,
output [99:0] out_different );
integer i;
always@(*)
begin
for(i=0;i<99;i++)
begin
out_both[i]=in[i]&in[i+1];
out_any[99-i]=in[99-i]|in[98-i];
out_different[i]=in[i]^in[i+1];
end
out_different[99]=in[99]^in[0];
end
endmodule
法2:使用assign语句
module top_module(
input [99:0] in,
output [98:0] out_both,
output [99:1] out_any,
output [99:0] out_different );
assign out_both=in[98:0]&in[99:1];
assign out_any=in[99:1]|in[98:0];
assign out_different={in[99]^in[0],in[98:0]^in[99:1]};
//或assign out_different=in^{in[0],in[99:1]};
endmodule
全加器
module top_module(
input a, b, cin,
output cout, sum );
assign cout=a&b|a&cin|b&cin;
assign sum=a^b^c;
endmodule
半加器
module top_module(
input a, b,
output cout, sum );
assign sum=a^b;
assign cout=a&b;
endmodule
此题与上一题对比,很容易想到这样一种实现方法 assign out = in[2*sel+3:2*sel];
verilog中这样的写法是错误的 ,verilog中要求向量下标可以是变量,
但是位宽必须是常量,而上式无法证明位宽是常量。
假设您有两个 8 位 2 的补码编号 a[7:0] 和 b[7:0]。这些数字相加得到 s[7:0]。此外,计算是否发生了 (signed) 溢出。
module top_module (
input [7:0] a,
input [7:0] b,
output [7:0] s,
output overflow
); //
assign s=a+b;
assign overflow=(a[7]&b[7]&~s[7])|(~a[7]&~b[7]&s[7]);
endmodule
当两个正数相加产生负结果或两个负数相加产生正结果时,会发生有符号溢出。
有几种检测溢出的方法:可以通过比较输入和输出数的符号来计算,或者从位 n 和 n-1 的进位推导出。
创建一个 100 位二进制加法器。加法器将两个 100 位数字和一个 carry-in 相加,以产生 100 位求和并执行。
module top_module(
input [99:0] a, b,
input cin,
output cout,
output [99:0] sum );
assign {cout,sum}=a+b+cin;
endmodule

被折叠的 条评论
为什么被折叠?



