赞
踩
I/O端口类型:
input wire型
output wire/reg型
verilog可综合语句 assign,always,其中initial 语句不建议用在可综合语句中。
关于时标:
`timescale 时间单位/时间精度
`timescale 1ns/1ps
#3 延时3ns,精度为1ps,递增时,可以递增0.1ns
如果 `timescale 1ns/1ns,精度为1ns,递增时只能按照1ns开始递增
变量类型:wire ,reg
wire 线型
reg 寄存器
在Verilog中,寄存器要用reg来声明,但是reg声明的变量不一定都是寄存器,有可能是锁存器,或者查找表。
查找表:可以理解为数电中的真值表
对于线型,用assign进行赋值即可。
对于寄存器,包含数据输入D端,时钟输入clk端,使能信号en(可无),数据输出Q端。其中,clk是指同步脉冲,一般是指外部晶振提供的方波时钟,占空比50%,即高低电平各占一个时钟周期的50%,1表示高电平,0表示低电平,高到低为下降沿,低到高为上升沿,一般都为上升沿有效时,触发寄存器(也可以下降沿触发,但是reg型必须在always块内进行编程);当时钟clk上升沿触发D端将数据传输至Q端,如果寄存器没有到达下一个沿,继续保持当前数据;当下一个沿到来时,再由D端传输新的数据至Q端。(不含使能情况,若含有使能,则需要在时钟有效后,使能有效,才进行数据的传输)
FPGA一般接收的是方波时钟,而非正弦波时钟。
RTL电路基本结构:组合逻辑 + 时序逻辑
RTL级描述的就是寄存器级的输出传输
阻塞赋值 = :属于立即发生
组合逻辑用,输出受限于输入信号最长延时路径,
非阻塞赋值 <=:时钟沿触发
时序逻辑用,输出受限于时钟沿的到来
在使用always时触发列表由posedge或negedge触发,使用非阻塞赋值
在使用assign时,用阻塞赋值
生成锁存器一般是电平触发 always @(*)
生成寄存器的一般是沿触发 always @(posedge clk)
在代码中进行数值定义时,如果不声明前面的位宽则默认为32bit,写代码时需要写明位宽和进制;如果数据表述为32位宽,但实际数值不足32位宽,则高位补0。
例如: 255 默认32bit
// reg [7:0] cnt = 8'd255;
// reg [3:0] cnt = 255; cnt 实际上等于 4'd15, 4hf,4'b1111
// reg [3:0] cnt = 81; cnt 实际上等于 4'b0001
//0~255 随机数生成方式 {$random}%256
// %表示求模运算,返回值是除以256后的余数
//always #10 a = {$random}%256;
赋值语句:
assign c1 = a & b; //c1 wire
always @(posedge clk) begin
c2 <= a & b; //c2 reg
end
//敏感列表不全会生成latch,所以建议直接使用 * 代替所有敏感变量
always @(*)begin
c3 <= a & b; // c3 reg
end
时钟频率计算:
时钟周期 20ns = 50 Mhz = 1 / 20
时钟周期 10ns = 100 Mhz = 1/10
F = 1/T
1s=10^3 ms=10^6 us=10^9 ns
1GHz=1000MHz,1MHz=1000kHz,1kHz=1000Hz
1s=1000ms,1 ms=1000μs,1μs=1000ns
时钟频率 | 时钟周期 |
1Hz | 1s |
1KHz | 1ms |
1MHz | 1μs |
1GHz | 1ns |
定义接口时,output reg c2=1'b0, 可给寄存器赋初始值;但是线网不可进行赋初始值。
运算符:
% 求模运算
假设得到0~9之间的随机数:a = {$random}%10;
判断 a 是否在 10 ~ 20 之间写法:
错误写法:if(10<a<20)
正确写法:if(10 < a && a < 20)
逻辑运算符(&&、||、!):结果为 1 或者 0
按位运算符(&、|、~):每一位都进行运算
条件运算符((x)?x:x)
移位运算发:
>>右移; 高位补0
<<左移;低位补0
位拼接运算符:{}; 例如:d <= {a,b,c};
位拼接实现循环左移:po_a <= {po_a[6:0],po_a[7]};
位拼接实现循环右移:po_a <= {po_a[0],po_a[7:1]};
移位运算应用于并串转换,SPI接口协议将协议以方波的形式传递出去(实际数据8bit,但SPI输出的数据是在一根线上,所以可以用移位寄存器将输入的8bit并行数据以串行方式以1bit输出方式串行输出)
条件判断语句:if…else case…endcase
PS:
在编写always块时,if else 叠加不易过多,不然可能造成线路的延时过多。因为,每一个 if else 语句都会生成一个选择器,而每两级选择器之间会存在线路延时,当 if else 过多时,选择器链路总延时就会很长,从而影响电路时序,当时序出问题时,就算是功能仿真正确,下板后电路也是不正确的。
另外:在使用 if else 时需要注意优先级造成的影响。
使用case…endcase语句可生成多路选择器,无延时,并行判断,勿漏default(组合逻辑中),不然会生成 latch:
对于:
else if(cnt == 4'd15)
cnt <= 4'd0;
如果cnt最大值是cnt位宽所能容纳的最大值时,计数到最大值后自动清零;
反之,如果位宽所能容纳的最大值不是cnt的最大值时,就需要加限制条件。
时钟分频:(在FPGA中,如果不调用PLL,则无法进行倍频)
在FPGA开发板上面只有一个晶振,即只有一种频率的时钟,如果需要用到不同频率的时钟,就需要在这个固定的时钟频率条件下进行分频或者倍频;
得到比固定的时钟频率更慢的时钟,进行分频操作;
得到比固定时钟频率更快的时钟,进行倍频操作;
时钟分频例子:呼吸灯
要求:从亮到灭的时间为 2s,从灭到亮的时间为 2 秒,完成呼吸的过程一共需要 4 秒时间。
解析:2s/1000份 = 2000000ms = 2000us,将2000us再分1000份=2us,
50Mhz=20ns,2us/20ns = 100,最小计数为100,计数1000次,得到一个2/1000 s,
计满1000个2/1000 s 即为2s,每2/1000 s 为1个PWM周期。
波形图:
- RTL code:
- module breathing_led(
- input wire clk,rst,
- output reg led
- );
-
- reg [6:0] clk50m_cnt;
- reg [9:0] clk50m_cnt_1000;
- reg [9:0] pwn_cnt_1000;
- reg pwn_flag;
-
- always @(posedge clk)begin
- if(rst)
- clk50m_cnt <= 7'd0;
- else if(clk50m_cnt == 7'd99)
- clk50m_cnt <= 7'd0;
- else
- clk50m_cnt <= clk50m_cnt + 1'b1;
- end
-
- always @(posedge clk)begin
- if(rst)
- clk50m_cnt_1000 <= 10'd0;
- else if(clk50m_cnt == 7'd99 && clk50m_cnt_1000 == 10'd999)
- clk50m_cnt_1000 <= 10'd0;
- else if(clk50m_cnt == 7'd99)
- clk50m_cnt_1000 <= clk50m_cnt_1000 + 1'b1;
- end
-
- always @(posedge clk)begin
- if(rst)
- pwn_cnt_1000 <= 10'd0;
- else if(clk50m_cnt == 7'd99 && clk50m_cnt_1000 == 10'd999 && pwn_cnt_1000 == 10'd999)
- pwn_cnt_1000 <= 10'd0;
- else if(clk50m_cnt == 7'd99 && clk50m_cnt_1000 == 10'd999)
- pwn_cnt_1000 <= pwn_cnt_1000 + 1'b1;
- end
-
- always @(posedge clk)begin
- if(rst)
- pwn_flag <= 1'b0;
- else if(clk50m_cnt == 8'd99 && clk50m_cnt_1000 == 10'd999 && pwn_cnt_1000 == 10'd999)
- pwn_flag <= ~pwn_flag;
- end
-
- assign led = (pwn_flag==1'b1)?((clk50m_cnt_1000 < pwn_cnt_1000)?1'b1:1'b0):((clk50m_cnt_1000 < pwn_cnt_1000)?1'b0:1'b1);
- endmodule
- TB:
- `timescale 1ns/1ns
- module tb_breathing_led;
- reg clk,rst;
- wire led;
-
- initial begin
- clk=0;
- rst=1;
- #100
- rst=0;
- end
-
- always #10 clk = ~clk;
-
- breathing_led inst_breathing_led (
- .clk(clk)
- , .rst(rst)
- , .led(led)
- );
-
- endmodule
Top-down设计
Top-down 设计即自顶向下的设计
在模块例化时:
如果模块内部接口是输入,连接的接口类型可以是 wire/reg;
如果模块内部接口是输出,连接的接口类型可以是 wire;
有限状态机
一段式指的是在一个 always块内使用时序逻辑既描述状态的转移,同时也描述数据的输出;
二段式指一个always 块使用时序逻辑描述状态转移,另外一个 always 块使用时序逻辑描述数据输出;
三段式指使用三个 always 块,一个 always 模块采用时序逻辑描述状态转移,一个 always 块采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 块描述状态输出(可以用组合电路输出,也可以时序电路输出)。
- //状态机模板
- module 模块名(
- 端口 1,
- 端口 2,
- ...
- 端口 N,
- );
-
- /时序逻辑描述的状态转移
- always@(posedge clk or negedge rst_n)
- 状态转移;
-
- //时序逻辑描述的数据输出
- always@(posedge clk or negedge rst_n)
- 数据输出;
-
- endmodule
对于modelsim自动化仿真脚本的建立:
- run.tcl
-
- #退出仿真
- quit -sim
- #清除临时保存的仿真文件
- .main clear
-
- #建立库
- vlib work
- #编译 .v
- vlog ./tb_test.v
- #编译所有 .v 到work库中
- vlog ./../src/*.v
- #启动仿真,-voptargs+=acc(等于手动点simulation),选择work库中的tb顶层文件,进行仿真
- vsim -voptargs+=acc work.tb_test
-
-
- #添加wave
- add wave /tb_test/test_init/*
- #执行1us
- run 1us
状态机例子:
一个自动售货机中的商品 2.5 元一件,每次投币既能投入 1 元, 也能投入 0.5 元,当投入 3 元时,找零0.5元,使用状态机描述。
- RTL code
-
- modele fsm(
- input wire clk,
- input wire rst,
- input wire pi_money,
- output reg po_money,
- output reg po_water
- );
- reg [4:0] state;
-
- parameter idle = 5'b00001;
- parameter half = 5'b00010;
- parameter one = 5'b00100;
- parameter one_half = 5'b01000;
- parameter two = 5'b10000;
- always @(posedge clk)begin
- if(rst==1'b1)begin
- state <= idle;
- end
- else begin
- case(state)
- idle : if(pi_money == 1'b1) state <= one;
- else if(pi_money == 1'b0) state <= half;
- half : if(pi_money == 1'b1) state <=one_half;
- else if(pi_money == 1'b0) state <= one;
- one : if(pi_money == 1'b1) state <= two;
- else if(pi_money == 1'b0) state <= one_half;
- one_half: if(pi_money == 1'b1) state <= idle;
- else if(pi_money == 1'b0) state <= two;
- two : if(pi_money == 1'b1) state <= idle;
- else if(pi_money == 1'b0) state <= idle;
- default : state <= idle;
- endcase
- end
- end
-
- always @(posedge clk)begin
- if(rst==1'b1)begin
- po_water <= 1'b0;
- po_money <= 1'b0;
- end
- else if(state == one_half && pi_money == 1'b1) || (state == two && pi_money == 1'b0)begin
- po_water <= 1'b1;
- po_money <= 1'b0;
- end
- else if (state == two && pi_money == 1'b1) begin
- po_water <= 1'b1;
- po_money <= 1'b1;
- end
- end
-
- endmodule
- tb:
- `timescale 1ns/1ns
- module tb_fsm;
- reg clk,rst,pi_money;
- wire po_money,po_water;
-
- initial begin
- rst = 1;
- clk = 0;
- pi_money = 0;
- #100
- rst = 0;
- end
-
- always #10 clk = ~clk;
- always #20 pi_money = {$random};
-
- fsm fsm_inst(
- .clk(clk)
- ,.rst(rst)
- ,.pi_money(pi_money)
- ,.po_money(po_money)
- ,.po_water(po_water)
- );
-
- endmodule
- run.tcl:
-
- quit -sim
- .main clear
-
- vlib work
- vlog ./tb_fsm.v
- vlog ./../src/*.v
- vsim -voptargs+=acc work.tb_fsm
-
- #定义结构体进行匹配
- virtual type{
- {5'b00001 idle}
- {5'b00010 half}
- {5'b00100 one}
- {5'b01000 one_half}
- {5'b10000 two}
- }abc;
- #创建新信号,(abc)强制转换
- virt function{(abc)/tb_test/test_init/state}new_state
- add wave /tb_fsm/fsm_init/*
- run 1us
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。