SYSTEM: ONLINE
VER. ...
SLEET LOG
SLEET'S LOG
/2021年12月11日/4 MIN READ

hdu数电实验入门指北

hdu verilog tutorial

前言

指北内容

  • ISE 的安装

  • 写实验要用到的基本语法&设计代码的思想

  • 实验的基本流程解读

面向人群

hdu 计科在读 verilog 速成人,仅供入门参考。因为这也是一个入门才两个月的菜写的(。

欢迎勘误:)

基础实验 1-15 代码参考

杭电计科数电基础实验 1-15

正文

ISE 的安装

https://pan.baidu.com/s/1LjkCsYhLFDcYiqq2KQPNPQ 提取码 987Q

安装好后会看到如下界面

a

点击 manage 之后,点 load 打开破解文件

win64 用户可能会碰到闪退问题,具体解决方法查一下就能找到~~,主要是忘了怎么解决的了~~

实验的基本操作流程

新手建议跟着老师下发的操作手册 pdf 走一遍流程。该 pdf 含有图形化界面的指引,相对友好

大概了解操作按钮方位后,可以跟着如下精简提示进行实验 ↓

  1. 创建工程 左上管理区任意位置右键,选择 New Source -> Verilog Module project settings 里配置 Device 为 XC7A100T, Package 为 FGG484,Speed 为-2L(一定要配对 setting,不然仿真/生成 bit 文件会生成不出来)(闪退相关具体问题见下)

  2. 检查语法 右下管理区 Synthesize-XST -> Check Syntax

  3. 编写测试代码 左上角最上一栏勾选 Simulation,在左上管理区任意位置右键,选择 New Source -> Verilog Test Fixture -> 选择要测试的模块 -> 写测试代码(测试编写方法见下)

  4. 仿真 确认左上角工程管理区 view 选项为 simualtion -> 选中仿真激励文件(左上角工作区中你要运行的 test.v) -> 左下角工作区 Simulation Behavior Model 启动仿真

  5. 配置管脚 勾选左上角工程管理区 view 选项为 implement ->左上管理区任意位置右键,选择 New Source -> Implementation-Constraints File -> 输入约束文件名,点击 Next (管脚编写方法见下)

  6. 生成二进制代码 左下区域右键选中 Generate Programming File -> Process Properties -> 在 General Options 页面勾选”-g compress” -> ok -> 双击左下区域的 Generate Programming File

可能出现的问题

创建工程闪退

file->new Project 之后,你可能会想更改文件位置(location)。然而如果你是 win64 用户,在点击 location 旁边的省略号后可能喜提闪退。此时可以将想存放的文件位置地址进行复制,直接粘贴到 location 中。

另外,在左上工作区右键add resource时,win64 也可能闪退。但是new resource是没问题的。

希望删除某一文件,实际上并没有删除

对文件右键点击remove,实际上并不是直接删除到回收站,而仅仅是将文件从工作区移除。所以再重新生成同名文件时会提示是否进行覆盖。

如何编写测试

如果某文件已生成测试文件后,又更改了其input output的变量,需要手动删除旧测试模块,再生成新的测试,因为测试模块不会自动随被测试文件更新。不过如果只是涉及测试模块的逻辑修改,一般不需要重新生成。

生成的测试文件将具有初始内容,测试时只需要在initial beginend之间填写测试代码即可。以下提供两类测试代码的书写方法。

  1. 直接指定特定数值。俗称打表

    verilog
    initial begin // Initialize Inputs mr = 0; load = 1; en = 0; dn = 0; clk = 0; d = 4'b0011; #10 clk=1; #20 load=0; en=0; clk=0; #30 load=0; en=1; dn=0; clk=1; #40 clk=0; #50 clk=1; #60 dn=1;clk=0; #70 clk=1; #80 mr=1; end
  2. 使用 forever begin,根据赋值时间间隔不断地自动生成不同的组合

verilog
initial begin // Initialize Inputs A = 0; B = 0; C = 0; D = 0; E = 0; // 这边的时间间隔建议互为质数,这样每个组合都能出现 forever begin #2 A=~A; #3 B=~B; #5 C=~C; #7 D=~D; #11 E=~E; end end

管脚配置

端口的选择

开关: T3 U3 T4 V3 V4 W4 Y4 Y6 W7 Y8 Y7 T1 U1 U2 W1 W2 Y1 AA1 V2 Y2 灯泡: K1 J1 H2 G2 F1 E2 D1 B1 B2 N3 M3 K3 H3 N4 L4 J4 按钮: R4 AA4 AB6 配置管脚的时候就根据这个来接端口

管脚文件解读

以五表决为例

verilog
// 这边是端口配置。A/B/C/D/E是开关,用于切换1/0,所以接到属于开关的端口 NET "A" LOC = T3; NET "B" LOC = U3; NET "C" LOC = T4; NET "D" LOC = V3; NET "E" LOC = V4; // Y是输出,所以接到灯泡的端口上 NET "Y" LOC = K1; // 如果是向量,比如input [1:0] Y。那配管脚是这样的: // NET "Y[1]" LOC = T3; // NET "Y[0]" LOC = U3; // 这边是所有的input和output都要写在这 NET "A" IOSTANDARD = LVCMOS18; NET "B" IOSTANDARD = LVCMOS18; NET "C" IOSTANDARD = LVCMOS18; NET "D" IOSTANDARD = LVCMOS18; NET "E" IOSTANDARD = LVCMOS18; NET "Y" IOSTANDARD = LVCMOS18; // 这边是所有的input(要开关的)写在这 NET "A" PULLDOWN; NET "B" PULLDOWN; NET "C" PULLDOWN; NET "D" PULLDOWN; NET "E" PULLDOWN;

实验中要用到的语法

变量声明

  • wire:没特别声明的默认类型

  • reg:如果要到 always 里对某个变量进行赋值,就要给变量指定为 reg 型

  • input: 没特别声明都是 wire 类型,不能为 reg 类型,用于定义输入的变量

  • output: 没特别声明都是 wire 类型,用于定义输出的变量

verilog
input a; // 默认是wire类型 output reg b; // 显式声明b为reg,可以在always代码块里对其进行赋值

代码块

使用示例如下:

verilog
if(a==b)begin a=0; end else begin a=1; end // 代码块里只有一个语句时,begin和end可以省略

赋值

非阻塞&阻塞赋值
verilog
// 阻塞赋值。执行c=a时a已经=b了 begin a=b; c=a; end // 非阻塞赋值。执行c=a时a还是原来的数值,等代码块结束之后a的值才会发生变化 begin a<=b; c<=a; end
如何对变量赋值
  • 初始化变量时进行赋值

  • 单独赋值时,需要用到assignalways

ps. assignalways不可混用,否则报错

assign

一般用于较简单的赋值&门级描述

verilog
assign b=(a==0)?c:d; // a=0时,使b=c,否则使b=d。只要b右边的式子里有变量变化,b就会被随时更新
always

一般用于行为描述

verilog
input a; output b; always@(a) // 括号里是指“当a发生改变时,执行该语句”。如果是想监听所有变量,则将`a`替换成`*` begin b=a; end

判断

两类判断语句都只能配合 always 使用

if-else
verilog
input a; output reg b; always@(*) begin if(a==1) begin b=1; end else if(a==0) begin b=1; end end
case
verilog
input a; output reg b; always@(*) begin case(a) 1: begin b=1; end 2: begin b=0; end endcase end

函数

定义函数
verilog
// add为函数名,并且在函数的书写中当成一个output变量使用。前面的位宽[4:0]是指定output的位宽 function [4:0] add; // 定义输入变量 input[3:0] a; input b; // 必须用begin/end将函数体包起来 begin // 可以直接写if/case之类必须在always里写的语句,并且将函数名作为output变量名使用 add={a,b}; end endfunction
使用函数
verilog
module demo(a,b,c); input[3:0] a; input b; output c; always@(*)begin c=add(a,b); end endmodule

时序逻辑电路的 posedge/negedge

写完 RS 之后,就要开始处理 clk 上跳触发的情况了,这种涉及时钟的情况必须使用 always 完成

verilog
input a,en,clk; output b; // clk上跳触发,且没有其他异步置位/清零的操作,则always的括号中仅写入clk,并指明是posedge上跳沿触发 // 如果有en进行异步清零,则在括号中还要写入en;没显式指明是异步清零,就不需要把en写在括号里 // 值得注意的是,和posedge clk一起写的时候,必须指定posedge还是negedge,即使en并非指明是上跳触发 // 指定posedege还是negedge的原则:假设en为1时异步清零,则将en指定为上跳触发(0->1确实是上跳沿,此时posedge生效的时候en必定为1) always@(posedge clk, posedge en) begin if(en==1) b=0; ... end

一点写法技巧

  • 判断 a=0 且 b=0
verilog
if({a,b}==2'b00)
  • 同时赋 a、b 为 0
verilog
// always代码块中某行代码 {a,b}=2'b00;
  • 切片赋值
verilog
// always代码块中某行代码 a[2:1]<=a[1:0]; a[0]<=a[2];

一个 verilog 代码的基本结构

以第一个实验五表决为例

verilog
module fiveSentense_1(A,B,C,D,E,Y); // 括号里是所有的input和output变量 input A,B,C,D,E; // 输入变量 output Y; // 输出变量 // 门级描述 wire A,B,C,D,E,Y; assign Y=((A&B&C)||(A&B&D)||(A&B&E)||(A&C&D)||(A&C&E)||(A&D&E)||(B&C&D)||(B&C&E)||(B&D&E)||(C&D&E)); endmodule

设计代码的基本思想

需要注意老师要求中有无限定实现方式。如 RS 触发器只能结构建模描述,否则 RS=11 时将会与预期行为有差别

行为描述

根据真值表来计算 output 的值,这种方法较为符合直觉。通常要用到 always 比如第三个实验译码器便可以利用行为描述

verilog
module decoder_3(g1,g2a,g2b,c,b,a,Y); input g1,g2a,g2b,c,b,a; output reg[7:0] Y; always@(*) begin // 行为描述 if(g1==0||g2a==0||g2b==0) Y=8'b11111111; else begin case({c,b,a}) 3'b000: Y=8'b11111110; 3'b001: Y=8'b11111101; 3'b010: Y=8'b11111011; 3'b011: Y=8'b11110111; 3'b100: Y=8'b11101111; 3'b101: Y=8'b11011111; 3'b110: Y=8'b10111111; 3'b111: Y=8'b01111111; endcase end end endmodule
结构描述

实际上 verilog 会根据你设定的 output 和 input 之间的关系连好电路,你只需要为 input 和 output 建立正确的表达式

结构描述就像连电路,语句主要涉及 assign

以第一个实验五表决为例

verilog
module fiveSentense_1(A,B,C,D,E,Y); input A,B,C,D,E; output Y; // 门级描述 assign Y=((A&B&C)||(A&B&D)||(A&B&E)||(A&C&D)||(A&C&E)||(A&D&E)||(B&C&D)||(B&C&E)||(B&D&E)||(C&D&E)); endmodule
Article Index