P1
P1_Study
《Verilog数字系统设计》语法部分学习
基本常识
- Verilog HDL程序是由模块构成的,每个模块的内容都是位于module和endmodule两个语句之间
- Verilog模块可以分为两种:
①使模块最终生成电路的结构
②测试模块testbench - 端口定义、IO说明、除endmodule外,每个语句和数据定义后必须有分号
模块结构
- assign用于建模组合逻辑,always用于建模时序逻辑或者组合逻辑,但是
always @*
有时候容易出问题,不建议使用,且区分不清reg到底是组合还是寄存器。组合逻辑比较复杂,可以使用assign + function
- 复位相关问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//规范的 同步复位:
always @(posedge clk) begin
if (reset) begin
end
else begin
end
end
//规范的异步复位
always @(posedge clk,posedge reset)
if (reset) begin
end
else begin
end
end
//如果是reset下降沿复位
always @(posedge clk,negedge reset)
if (~reset) begin
end
else begin
end
end - 并行性
①在Verilog中所有过程块(initial、always)、连续赋值语句assign、实例引用都是并行的,这三者出现的先后顺序没有关系。
②initial语句只执行一次,always语句一直重复活动直至仿真结束。
③在always模块内部,逻辑是顺序进行的。
④只有连续赋值语句assign和实例引用语句可以独立于过程块而存在于模块的功能定义部分。
数据类型
- 数字
<位宽><进制><数字>8'b10101010
<进制><数字>默认位宽至少32位
<数字>默认十进制
b2 o8 d10 h16
负数在位宽前加负号-8'b10101010
每个字母用8位ASCII码值表示 - 变量
首先说明,电路中存在的信号就是reg和wire
①wire型变量,网络数据类型,表示结构实体(例如门)之间的物理连接,不能储存值,而且必须受到驱动器(例如门或者连续赋值语句assign)的驱动。如果没有驱动器连到网络型数据类型变量上,该变量就为高阻z。
Verilog程序模块中输入输出信号默认为wire型,wire可以作为任何方程式的输入,也可以用作assign语句或者实例元件的输出
②reg型变量,对应寄存器单元,通常用来表示always模块内的指定信号。对reg型变量赋值就像改变了一组触发器的存储单元的值。
需要注意reg型数据可以赋负值,但当reg型数据是一个表达式中的操作数时,它被当作无符号数,也就是正值。
always中的每一个信号必须被定义成reg型。
数据的正负
在电路中所有的数都是一个二进制数,有没有符号,只是我们去解读它的一个视角,而不是这个数本身的属性。③integer型变量,reg和wire才是标准电路里用到的,integer可以做循环变量(虽然reg型变量也可以做循环变量),这样可以更好的区分电路里的寄存器和循环变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17reg [7:0] rega = 8'b11111111;
//有符号-1
//无符号255
//如果想要这个数以有符号形式比较大小或者移位运算
wire cond;
assign cond = $signed(rega) > $signed(-2);
//把带signed的表达式拆分成中间变量来写,是一定不会出错的;
//容易犯的错误:三目运算中间用$signed,搞不清楚Verilog优先级由不加()
if (cond) begin
//......
end
//如果有符号就看成补码,判断相等以及加减运算其实与符号无关
a = 8'b00001010 (8'd10) + 8'b11111111;
与 + (-1)其实没有区别
top == -1;//同理 都以补码存储
Verilog被综合成电路,是n份循环体串联或并联起来,循环变量在电路中就被展开了,是实际上不存在的。
注意: 课上题目以及后面写CPU不要轻易尝试automatic递归,不可综合,甚至就不要用Verilog去写递归,用硬件语言是去描述硬件的。 - 赋初值问题写Verilog,永远用reset来给寄存器赋初值,如果不是特别必要说明,initial都不是必要的。(除了课下的部分题目因为年代久远,可能某些测试点忘记复位信号)
运算
- 逻辑右移>>,算数右移>>>,合理适当使用
$signed()
以及位拼接运算符{}
- 非阻塞赋值
b <= a
在编写可综合程序时,这是最常用的方法,always建模时序逻辑一般都使用非阻塞赋值 - 阻塞赋值
b = a
条件语句
1 | begin |
条件语句必须在过程块语句中使用(initial/always),并且在条件语句中也要合理使用begin-else,避免不必要的错误
case语句
1 | case(rega) |
循环语句
1 | //初始化memory |
其他
- Verilog中控制时序和语句执行顺序的三种方式:
①基于延迟的时序控制
②基于事件的时序控制
③电平敏感的时序控制 - 我们一般使用的begin-end块是顺序块,也可以给块起名
1
2
3begin: BLOCK_NAME
//......
end - 把输出变量声明为寄存器类型(maybe用的上)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22module mux4_to_1 (out,i0,i1,i2,i3,s1,s0);
output out;
input i0,i1,i2,i3;
input s1,s0;
//将输出声明为寄存器
reg out;
//组合逻辑建模
always @(s1,s0,i0,i1,i2,i3)
begin
case({s1,s0})//位拼接
2'b00 :out = i0;
2'b01 : out = i1;
2'b10 : out = i2;
2'b11 : out = i3;
default: out = 1'bx;
endcase
end
endmodule
P1 教程学习
splitter
- 注意模块命名
- 利用A[m:n]读取相应的位
ALU
注意$signed()和三目运算符的使用
1 | assign C = (ALUOp == 3'b000) ? A + B : |
EXT
- 注意操作数和功能序号的区别
- $signed() 逻辑右移 算数右移
1
2
3
4
5assign ext = (EOp == 2'b00) ? {$signed(imm) >>> 16,imm} :
(EOp == 2'b01) ? {16'b0,imm} :
(EOp == 2'b10) ? {imm,16'b0} :
(EOp == 2'b11) ? {$signed(imm) >>> 16,imm} << 2 :
32'b0 ;
gray
本质是时序电路:同步复位、状态转移,把格雷码当成对应的状态输出
1 | always @(posedge Clk) begin |
Verilog FSM
- 逻辑思考,状态编码,画出状态转移图
- `define or parameter表示状态
- 根据状态转移图构建次态逻辑与输出逻辑
- 写代码:
①同步复位or异步复位,复位一定要回到最原始状态
②合理使用if-else和case语句(case语句后一定要加上default语句,避免锁存器产生) - 注意输出是Moore还是Mealy,可以根据波形是完整一个周期的置高还是立即置高来判断
expr
- Moore机且异步复位
- 注意逻辑思考,其实可以化成两种输入(数字or符号),四种状态(nothing or F or (F + <符号>)or 不可能再成为F)其中F遇到数字、(F + <符号>)遇到符号、nothing遇到符号,都是不可挽救状态
BlockChecker
- 首先是想清楚有几种输入,几种状态
- 这里使用栈存储begin,需要注意的是撤销end或者begin的操作(这里是对end的操作有误)
自己可以多试几种数据比如,end begin endc ,begin end beginc 这样的
本地要多做测试 - 其他错误:reset没有完全初始化、define中内容打错
易错点
- 引用宏定义记得加
`
- 状态转移搞对,不要马虎,想清楚需要什么状态,怎么转移
- begin-end对应好,可以打一个begin打一个end,再填写中间内容,以及墙裂建议使用VScode
VoterPlus
1 | module vote7( |
注意:在时序逻辑中不可以对同一个变量用for循环多次非阻塞赋值,最终其实只有效赋值一次,合理的解决办法:
①利用时序逻辑改变状态,存储状态,不存在重复赋值(例如逐位清零、置1等)
②利用组合逻辑可以重复对一个变量赋值,时刻变化。
祝帆帆P1上机顺利!加油!
P1_review
首先恭喜顺利通过P1!
奇偶校验
这个与往年题一样,如果符合就最高位原样输出,如果不符合就把最高位取反。
字符串检查
不要想多了,就是一道Moore机题,“111110”状态disc置高,“1111110”状态flag置高,“1111111+”状态err置高
如果你写成mealy机会发现只置高半个周期,显然不符合题意,应该是Moore机
旅鼠
- 就是hdlbits上的lemmings3,感觉不难,按状态分析就好,课下交hdlbits也过了(可能课上的if都没写最后的else?要避免锁存器的产生)
- 写testbench的时候一定要注意不能在上升沿改变input信号的值,要错开
- Post title:P1
- Post author:Coooookie
- Create time:2023-01-02 20:29:37
- Post link:https://coooookie0913.github.io/2023/01/02/P1/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.