赞
踩
第一章:VERILOG速览
1.1 数值表示:以8‘b10_110_110为例,8指的是位宽,b指的是数据类型为2进制,可以用下划线或者空格作数值分隔,不影响数值大小。此外,字符串类型使用8bit ASCLL值表示。
1.2 关于宽度声明:
在Verilog里面,一般用reg来存储字符串。例如要存储4个字符的字符串,变量应该声明为reg [31:0] str。因为每个字符的宽度为8位,4个字符一共32位。最高位为第31位,最低位为第0位。
如何初始化一个宽度声明的变量?
[ SIZE,1] 或者 [ SIZE - 1 ,0],SIZE为位宽
字符串在声明时可以给定初始值:reg [31:0] str = "this"
如果我们想要改变字符串中的某个字符,比如更改最后一个字符(称为第0个字符),可以写成 str[7:0] = "d",字符串就变成了"thid"。为什么呢?
1.3 特殊迷惑符号:
$:一般指系统任务或系统函数,是verilog标准的一部分,了解这部分的存在即可。
【FPGA篇章七】FPGA系统任务:详述常用的一些系统函数以及使用方法 - 你好24h - 博客园
{ }:位拼接符号,如a = 1’b1,b = 1‘b0,c = 1’b1 ,{ a, b, c, } = 3'b101
1.2 输入输出信号:
input 模块的输入信号,比如 input Clk。
output 模块的输出信号,比如 output[3:0]Led。
inout 模块的输入输出双向信号,默认为wire型,只能采用assign赋值语句,不能在always中使用。在综合时是以三态门的形式存在的,这个基本用不到,先不讲,
1.3 信号类型:
wire 线信号,一般用于assign语句。
reg 寄存器,一般用于always块中。
组合逻辑电路在逻辑功能上的特点是任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关,不涉及对信号跳变沿的处理,无存储电路,也没有反馈电路。通常可以通过真值表的形式表达出来。 assign可以配合条件符 ?实现一些简单的组合逻辑,如:
assign out scl ?in1:in2
但是对于比较复杂的组合逻辑,用assign需要和?层层嵌套,不具有可读性,不如使用对任意书变量敏感的always表示组合逻辑,尽管always使用的信号需要定义为reg,但是由于没有时钟的参与,输出与输入唯一相关,没有状态的差异,所以此处的reg并不是真正的寄存器。
P.S. 寄存器是用来存放数据的小型区域,对自己的数据有记忆功能,能够储存电路当前的状态,自然是时序逻辑电路。
modelue是verilog的基本描述单位,实际上等同于一个电路器件。
模块有五个部分:端口定义、参数定义、I/O说明、内部信号声明、功能定义
以一个信号跟随电路为例:
4.1 概念:
连续赋值:assign只能用于wire 类型赋值,assign 中必须用阻塞赋值
连续赋值assign语句独立于过程块,所以不能在always过程块中使用assign语句
过程赋值:initial 或 always 语句块中的赋值,可以使用阻塞赋值和非阻塞赋值
阻塞赋值:=,顺序执行,若赋值表达式右端无延时,立刻赋值
非阻塞赋值:<=,并行执行,先进入调度队列,等到事件被调度时,若没有指定赋值的延迟,赋值会在最后时刻发生,关联”块“的概念。
能不能用阻塞赋值实现非阻塞赋值的效果呢?(提示:always的并发执行)
4.2 assign、always、initial:
关于assign:assign是连续赋值,不能带时钟,一般用于连接两个变量,将一个变量的值不断赋给另一个变量,如在顶层模块中调用另一模块的变量。always和initial是过程块
关于always:
可以执行多次
always是过程赋值,可以带时钟
不带时钟时always和assign完全一致,虽然信号定义仍为reg,但产生的依然是组合逻辑(无时钟)
always@( 填后边的词 ) // posedge XXX(上升沿)、 negedge XXX(下降沿) 、*(自动识别敏感信号列表)、信号名字(组合逻辑写法)
特别注意:
1. 块内的逻辑描述要与敏感信号列表中的信号的有效电平一致。 ?
2. 在verilog2001标准中,补充说明用逗号和or分隔敏感信号列表的效果一致。
敏感信号列表中,逗号和or有什么区别?电平事件(组合逻辑)和边沿事件(时序逻辑)
Verilog 电平敏感时序控制_zhouyiiii11111的博客-CSDN博客_verilog 电平敏感
Verilog初级教程(10)Verilog的always块_李锐博恩的博客-CSDN博客_always verilog
关于initial:
只能执行一次
4.3 赋值延迟:
首先讨论 timescale 对于时间的影响:
timescale包含时间单位和时间精度两部分,设置格式为 `timescale timeunit / timeprecision
由值和单位组成,值:1、10、100;单位:s、ms、us、ns、ps、fs。时间精度不能比时间单位大!
如果设置`timescale 1ns / 1ps ,则#100是相对时间,等价于#100ns。
timescale的作用范围是这一指令后边所有模块的时延值,直至遇见另一个timescale或者resrtall(复位)。
verilog延迟语句可以在赋值运算符的左侧或者右侧指定延迟:
左侧:#<delay> <LHS> = <RHS> 右侧:<LHS> = #<delay> <RHS>
延迟语句看这个,讲的好!:
Verilog初级教程(22)赋值间延迟语句与赋值内延迟语句_李锐博恩的博客-CSDN博客_verilog延时赋值
begin 和 end ,其中的语句顺序执行,非阻塞赋值除外(块结束后执行)
fork 和 join ,其中的语句并行进行
case:全等比较:敏感表达式中与各项值之间的比较是一种全等比较,每一位都相同才认为匹配。
casez:表达式中某些位的值为高阻z,那么对这些位的比较就会忽略,不予考虑,而只关注其他位的比较结果。
casex: 表达式中某些位的值为高阻z或者不可知x,那么对这些位的比较就会忽略,不予考虑,而只关注其他位的比较结果。
4’b???0:?位不做比较
不可综合:case中使用x(不可知态),z(高阻态)作为比较值,以及casex,casez
宏定义:用一个代号代替一个复杂名称、字符串、或者表达式
`define sum ina+inb+inc
文件包含:`include “文件名”,将一个文件完全包含在另一个文件里。
条件编译:
`ifdef 宏名
语句块
`else
语句块
`endif
当宏名被定义时,对应的语句块参与源文件的编译
层级:module > task > function
三种形式有细微差距:
module是verilog的基本单位,可以调用其他module、task、function。
task是阉割版的module,不能使用initial 和always等过程赋值,可调用其他task,自身,function
function是最低级,只能用来组合逻辑,不可调用task和module,不过存在递归用法,返回值默认为reg型(函数返回的是一个状态,想当然的理解为返回值是寄存器类型)
格式:
module 模块:
module balabala(in,out,clock);
input [7:0] in;
input clock;
output [7:0] out;
reg [7:0] out;
always @(posedge cloclk)begin
#1 out = in >>1;
end
endmodule
task 任务:
task balabala;
input in1,in2;
output out1,out2;
#1 out1 = in1&in2;
#2 out2 = int | in2;
endtask
调用方式为:balabala(data1,data2,data3,data4)
实际上data1-data4就对应task内部的in1-out2
function函数:
function [7:0] balabala;
一些语句块
balabala被赋值
endfunction
特别的,函数的定义中存在一个与函数同名的寄存器,也即函数名本身就是一个寄存器,最终balabala最终赋予的值就是就是该函数的返回值。
9.1 原理:
9.2 实现过程:
按always模块的数量:一段式、二段式、三段式,各有优劣。
具体如何实现嘞?参考数字系统设计与VerilogHDL(第七版)p229,用状态机控制LED。
第二章:经典例程(点灯及串口收发)
流水灯例程:
首先电机Create Project 创建新的工程模板,如果有创建好的工程模板也可以点击Open Project
确认信息界面,直接Next跳过
这里填写工程名称和工程存放位置,默认是C盘,最好改一下
此处为工程类型选择,有RTL工程,导入工程,IO规划工程,示例工程等,这里默认选RTL工程
此处为文件添加界面,我们不添加任何源文件,如果需要添加,工程创建好之后也可以再加
此处为约束文件添加界面,包含时序约束和管脚约束,我们不添加
此处选择自己的芯片型号,按需选取即可
确认信息无误,生成工程!
点此添加源文件
我们要添加一个设计文档
创建文件
语言类型一般都选Verilog吧,国内都是Verilog,欧洲比较喜欢VHDL,华为偏爱SystemVerilog
名字随意起
选一下默认生成的模块名称吧,默认和.v文件是一样的,led_test很符合我们的需求,可以不改
设计文件生成好了,但是这个时候字特别小看不清,我们可以在Settings-Text Editor-Fonts and Colors里调整文字大小
工程生成到此为止。
`timescale 1ns / 1ps
//省略若干无用声明信息
module led_test(
//首先定义输入输出端口如下
input clk,
//时钟输入为板载50MHz晶振,对应引脚为K17
input rst_n,
//复位信号为板载按键PL-KEY3,对应FPGA引脚为M19(PL-KEY2),ARM和FPGA的外设用PS和PL加以区分
output reg[3:0]led_state
//输出为4颗LED的电平状态,故定义一位宽为4的wire型(默认为wire),对应引脚为T12、U12、V12、W13
);
reg [31:0]cnt;
//定义一个32位宽寄存器,用以存放时钟脉冲
reg [1:0]led_on_number;
//定义1个2位宽寄存器,用以选择点亮某颗LED
//为什么是reg型:1.always过程赋值必须用reg;2.LED点亮为时序逻辑电路,需用寄存器存储系统某一时刻状态
parameter CLOCK_FREQ = 50_000_000;
//定义一个参数,即一秒内晶振输送的脉冲个数,该参数不实际使用
parameter CLK_NUM_NEED = CLOCK_FREQ/2 - 1;
//定义一个参数,数值为24999999,对应0.5S内晶振输送的脉冲个数,24999999与0之间有25000000个数
always@(posedge clk,negedge rst_n)begin//此处敏感信号列表为边沿信号,收到对应边沿触发
if(!rst_n)begin//一旦复位按键按下,无论是否收到clk脉冲都清零,此处为异步清零
cnt<=32'd0;//脉冲计数数量清零
led_on_number<=2'd0;//led2-4熄灭,led1亮
end
else begin//如果复位按键未按下且收到一个新的clk上升沿,此处为同步赋值
cnt<=cnt+1'b1;//脉冲个数累加
if(cnt == CLK_NUM_NEED)begin
cnt<=32'd0;
led_on_number <= led_on_number + 1'b1;
end
end
end
always@(led_on_number)begin//此处敏感信号为电平信号,改变即触发
case(led_on_number)
0:led_state<=4'b0001;
1:led_state<=4'b0010;
2:led_state<=4'b0100;
3:led_state<=4'b1000;
endcase
end
endmodule
写好编译一下
右上角提示综合中,得稍微等一会,根据电脑性能时间有长有短
综合完成,但是不要点击OK(symthsis:综合、implementation:执行),因为还没有添加管脚约束信息,直接cancel退出
打开约束设计(Elaborated Design),进行管脚约束
约束好之后是一幅原理图,暂时没啥用,我们切换到IO配置界面
进行配置,管脚号选择参考原理图,电平标准统一选LVCMOS333
对这两列进行配置如图
配置完成,CTRL+S保存一下
现在要生成比特流了,比特流就是我们要烧录到FPGA中的文件类型(烧录该类型掉电后会全部消失,想要掉电重启依然运行预定程序的方法是固化,这个之后再讲)
生成比特流中,电脑又呼啸起来了
生成完毕,我们直接打开硬件管理器,连接我们的FPGA设备
这里由于我使用的是ZYNQ异构板卡,VIVADO会同时识别到一个FPGA设备和一个ARM设备
我们不管这么多,直接Program device,烧录程序!
串口收发例程:不用讲了
第三章:纯逻辑FPGA-VGA
(贪吃蛇没时间了,做一个简单点的飞机大战)
一、原理部分:
预备知识:VGA实验 / VGA转HDMI(IP核)(9-24讲)
贪吃蛇工程讲解及复现(9-25讲)
使用的VGA格式:1024*768@60Hz
VGA是什么?一种接口、一种视频传输标准(模拟信号)
我们要学什么?
VGA的接口结构?
一种D型接口,其中比较重要的是三基色彩色分量以及2根扫描同步信号hsync(行同步信号,表示扫描一行的开始)和vsync(帧同步信号,表示扫描一帧的开始),其中三基色信号分量的电平标准是RS343,峰值电压是1V。
VGA的显示原理?
显示器一般由CRT(阴极射线管)构成,显示采用逐行扫描的方式解决,阴极射线管发射电子束到达含有荧光粉的显示屏上,形成彩色像素。
扫描方式:从上到下、从左到右。每扫完一行,用行同步信号进行同步,对CRT进行消隐(行消隐,也称水平消隐,指扫描位置从旧行的结束转移到新行的开始的调整过程;场消隐,也称垂直消隐,指扫描位置从旧帧的右下角专转移到新帧的左上角的调整过程)。消隐期间RGB输出为000.
扫描方式分为逐行扫描和隔行扫描。逐行扫描:扫描完所有的行,用场同步信号进行CRT场消隐,返回一帧图像。隔行扫描:每隔一行扫一线,完成一屏后返回扫描剩余一帧扫描所需的时间称为垂直扫描时间,其倒数称为场频率,即刷新一帧的频率。
行同步以时钟为单位,场同步以行为单位
标准场频率为60Hz,行频为31.5Hz,如不遵循显示屏支持的标准,可能损害显示器。
VGA的时序控制?
HSync:拉高脉冲,作为下一行开始的标志
VSync:拉高脉冲,作为下一帧的开始
RGB:3:3:2,所需色彩带宽为8位,可表示256种颜色
PCLK(Pixel Clock)像素时钟脉冲,Pixel clock 会将每一条水平线分成取样的样本,越高频率的 Pixel clock,每条扫瞄线会有越多的样本画素。
时钟频率要求:65MHz(1024*768@60Hz)如何计算?
显示后沿/显示前沿(也称后肩或者前肩):行同步或场同步信号发出后,视频数据不能立即使能,要留出电子枪回扫的时间。以行扫描为例,从HSYNC结束到DE开始的区间成为行扫描的后肩,从DE结束到HSYNC开始称为前肩。同样对于场扫面也可以由类似的定义。
程序设计:
FPGA内部生成VGA格式,调用VGA转HDMI的IP核生成HDMI格式输出(受限于板卡上只有HDMI接口)
以1024*768*60Hz为例
对于行信号:同步脉冲136、显示后肩160、显示区域1024、显示前肩24,合计帧长1344
对于场信号:同步脉冲6*1344,、显示后肩29*1344、显示区域768*1344、
如图,真正的显示区域是在行同步信号处于显示区域,且场同步信号也处于显示区域时产生,在其他区域时,红绿蓝基色都要给低电平
基准时钟(像素时钟)如何计算?
由于刷新率为60Hz,因此1s内显示60幅图像,即1幅图像的显示时间是1/60s,一幅图像占用了806*1344个基准时钟脉冲,因此基准时钟周期应为(1/60)/(806*1344)s,即1.53856*10-8s,1/1.53856*10-8 = 64995840,时钟频率约等于65MHz,我们使用的开发板板载外部晶振为50MHz,达不到要求,需要进行时钟倍频分频
二、工程部分
关于本工程使用到的IP核:
常用IP核介绍:Clocking Wizard,该IP核可以将输入的时钟信号进行倍频,倍数可以大于1,也可以小于1
输入时钟的配置:
输出时钟的配置:
配置完成后,点击OK,点击GENERATE,即可例化调用该IP核,获得所需频率时钟
//对时钟倍频IP核进行调用,
clk_wiz_0 clk_wiz_0_inst(
.clk_out1(clk_65MHz),
.clk_out2(clk_371p25MHz),
.locked(locked),
.clk_in(clk),
.resetn(rstn)
);
2. 调用VGA转HDMII的IP核
RGB_TO_DVI模块的使用:
HDMI RGB_TO_DVI模块_布丁的FPGA之旅的博客-CSDN博客_rgb转dvi
关于为什么TMDS(最小化传输差分信号)需要的编码时钟是5倍频(相对于像素时钟·)
如图所示,该IP核顶层模块分为encoder和serializer两个子模块,前者用以实现8位(例如R:G:B=3:3:2)(2到10位的编码转换),后者用来实现以上位数的加串(需要调用OSERDESE原语,即输出 并-转-串 转换器,同时使用5倍频,因为该原语本身具有2倍频的功能,所以可以实现传输时间上的均衡)
《UG471》中文翻译(3)OSERDESE2原语介绍 - 知乎
Xilinx原语OSERDESE2的使用和仿真详解-电子发烧友网
问题:什么是原语?
回答:一些功能最简、结构最简的电路模块,可直接例化使用,可以理解为XILINX提供的库函数。
IP核使用:
PIXELCLK如何确定?
对于720P的视频:
行:共1650个像素,有效像素1280,370个blank像素
列:共750个像素,有效像素720个,30个blank像素
像素时钟计算:1650*750*60 = 74.250MHz
对于1024*768@60Hz:
像素时钟计算:806*1344*60 = 64995840,约等于65MHz
代码详解:
module vga_sync
(
output hsync,
output vsync,
output ready,
output [10:0] x_addr,
output [10:0] y_addr,
input clk,
input rstn
);
parameter H_FRONT_PROCH =24;//行同步信号:显示前肩
parameter H_SYNC_TIME =136;//行同步信号:同步脉冲
parameter H_BACK_PROCH =160;//行同步信号:显示后肩
parameter H_ADDR_TIME =1024;//行同步信号:显示区域
parameter H_TIME_TOTAL=H_FRONT_PROCH+H_SYNC_TIME+H_BACK_PROCH+H_ADDR_TIME;
//行同步信号:一行所有的时钟脉冲个数
parameter H_ADDR_START_PIX =H_FRONT_PROCH+H_SYNC_TIME;
//行同步信号:显示区域开始标识
parameter H_ADDR_END_PIX =H_FRONT_PROCH+H_SYNC_TIME+H_ADDR_TIME;
//行同步信号:显示区域结束标识
//24+136+160+1024 = 1344,单位:时钟脉冲
parameter V_FRONT_PROCH =3;//场同步信号:显示前肩
parameter V_SYNC_TIME =6;//场同步信号:同步脉冲
parameter V_BACK_PROCH =29;//场同步信号:显示后肩
parameter V_ADDR_TIME =768;//场同步信号:显示区域
//3+6+29+768 = 806,单位:行
parameter V_TIME_TOTAL=V_FRONT_PROCH+V_SYNC_TIME+V_BACK_PROCH+V_ADDR_TIME;
//场同步信号:一帧所有的行的个数
parameter V_ADDR_START_PIX =V_FRONT_PROCH+V_SYNC_TIME;
//场同步信号:显示区域开始标识
parameter V_ADDR_END_PIX =V_FRONT_PROCH+V_SYNC_TIME+V_ADDR_TIME;
//场同步信号:显示区域结束标识
//行同步信号计数
reg [12:0] cnt_hreg;
always @(posedge clk, negedge rstn) begin
if(!rstn)
cnt_hreg <= 'd0;
else if(cnt_hreg == H_TIME_TOTAL-1) //H_TIME_TOTAL PERIOD CNT
cnt_hreg <= 'd0;
else
cnt_hreg <= cnt_hreg + 'b1;
end
//列同步信号计数
reg [12:0] cnt_vreg;
always @(posedge clk, negedge rstn) begin
if(!rstn)
cnt_vreg <= 'd0;
else if(cnt_vreg == V_TIME_TOTAL-1) //V_TIME_TOTAL PERIOD CNT
cnt_vreg <= 'd0;
else if(cnt_hreg == H_TIME_TOTAL-1)
//行消隐时列同步计数信号自增(因为换了一行,列同步信号以行为单位)
cnt_vreg <= cnt_vreg + 'b1;
end
//判断是否处于显示区域内(认为行同步信号和列同步信号同时处于显示区域时,处于图像显示区域,作为RGB信号输出判据)
reg ready_reg;
always @(posedge clk, negedge rstn) begin
if(!rstn)
ready_reg <= 'b0;
else if( (cnt_hreg >= H_ADDR_START_PIX && cnt_hreg < H_ADDR_END_PIX)
//H PIX VALID
&& (cnt_vreg >= V_ADDR_START_PIX && cnt_vreg <V_ADDR_END_PIX) )
//V PIX VALID
ready_reg <= 'b1;
else
ready_reg <= 'b0;
end
//各种标志位
//assign critical signal
assign hsync = (cnt_hreg < H_FRONT_PROCH) ? 'b0 : 'b1;
assign vsync = (cnt_vreg < V_FRONT_PROCH) ? 'b0 : 'b1;
assign ready = ready_reg;
assign x_addr = ready_reg ? cnt_hreg - H_ADDR_START_PIX : 'd0;
assign y_addr = ready_reg ? cnt_vreg - V_ADDR_START_PIX : 'd0;
RB渐变
endmodule
module color_generate(
output reg [7:0] R,
output reg [7:0] G,
output reg [7:0] B,
input clk,
input rst_n,
input [10:0] col_addr,
input [10:0] row_addr,
input ready
);
always @(posedge clk, negedge rst_n) begin
if(!rst_n)begin
R<='d0;
G<='d0;
B<='d0;
end
else begin
R<='d256-col_addr[7:0];
//R渐变信号来源:col_addr—cl_adr—x_addr
//x_addr = ready_reg ? cnt_hreg - H_ADDR_START_PIX : 'd0;
G<='d256-row_addr[7:0];
G渐变信号来源:row_addr—rw_adr—y_addr
//y_addr = ready_reg ? cnt_vreg - V_ADDR_START_PIX : 'd0;
B<='d256-col_addr[7:0];
//B渐变信号来源:col_addr—cl_adr—x_addr
//x_addr = ready_reg ? cnt_hreg - H_ADDR_START_PIX : 'd0;
end
end
endmodule
input clk, //pixel clock
input rstn, //reset signal high active
output hs, //horizontal synchronization
output vs, //vertical synchronization
output ready, //video valid
output[7:0] rgb_r, //video red data
output[7:0] rgb_g, //video green data
output[7:0] rgb_b //video blue data
);
wire [10:0] cl_adr;
wire [10:0] rw_adr;
vga_sync vga_sync_inst(
.hsync(hs),
.vsync(vs),
.ready(ready),
.x_addr(cl_adr),
.y_addr(rw_adr),
.clk(clk),
.rstn(rstn)
);
color_generate color_generate_inst(
.R(rgb_r),
.G(rgb_g),
.B(rgb_b),
.clk(clk),
.rst_n(rstn),
.col_addr(cl_adr),
.row_addr(rw_adr),
.ready(ready)
);
endmodule
module hdmi_encoder_top
(
output wire[2:0] TMDS_DATA_P1,
output wire[2:0] TMDS_DATA_N1,
output wire TMDS_CLK_P1,
output wire TMDS_CLK_N1,
output wire HDMI_OUT_EN1,
input wire clk,
input wire rstn
);
wire clk_65MHz;
wire clk_371p25MHz; // 74p25 x 5
wire locked;
//对时钟倍频IP核进行调用,
clk_wiz_0 clk_wiz_0_inst(
.clk_out1(clk_65MHz),
.clk_out2(clk_371p25MHz),
.locked(locked),
.clk_in(clk),
.resetn(rstn)
);
rgb2dvi_0 rgb2dvi_0_inst1(
.TMDS_Clk_p(TMDS_CLK_P1),
.TMDS_Clk_n(TMDS_CLK_N1),
.TMDS_Data_p(TMDS_DATA_P1),
.TMDS_Data_n(TMDS_DATA_N1),
.aRst(1'b0),
.vid_pData({Red, Blue, Green}),
.vid_pVDE(ready),
.vid_pHSync(Hsync),
.vid_pVSync(Vsync),
.PixelClk(clk_65MHz),
.SerialClk(clk_371p25MHz)
);
assign HDMI_OUT_EN1 =1'b1;
wire [23:0]rgb_data_wire;
(*mark_debug="true"*) wire pix_valid_wire;
(*mark_debug="true"*) wire [10:0]pix_x;
(*mark_debug="true"*) wire [10:0]pix_y;
vga_driver vga_driver_inst(
.clk(clk_65MHz), //pixel clock
.rstn(rstn), //reset signal high active
.hs(Hsync), //horizontal synchronization
.vs(Vsync), //vertical synchronization
.ready(ready), //video valid
.rgb_r(Red), //video red data
.rgb_g(Green), //video green data
.rgb_b(Blue) //video blue data
);
endmodule
这是1920*1080*30Hz的效果,1024*768@60Hz还没有调好,可能是屏幕不支持
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。