P1

Coooookie hahah

P1_Study

《Verilog数字系统设计》语法部分学习

基本常识

  1. Verilog HDL程序是由模块构成的,每个模块的内容都是位于module和endmodule两个语句之间
  2. Verilog模块可以分为两种:
    ①使模块最终生成电路的结构
    ②测试模块testbench
  3. 端口定义、IO说明、除endmodule外,每个语句和数据定义后必须有分号

模块结构

  1. assign用于建模组合逻辑,always用于建模时序逻辑或者组合逻辑,但是always @* 有时候容易出问题,不建议使用,且区分不清reg到底是组合还是寄存器。组合逻辑比较复杂,可以使用assign + function
  2. 复位相关问题
    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
  3. 并行性
    ①在Verilog中所有过程块(initial、always)、连续赋值语句assign、实例引用都是并行的,这三者出现的先后顺序没有关系。
    ②initial语句只执行一次,always语句一直重复活动直至仿真结束。
    ③在always模块内部,逻辑是顺序进行的。
    ④只有连续赋值语句assign和实例引用语句可以独立于过程块而存在于模块的功能定义部分。

数据类型

  1. 数字
    <位宽><进制><数字> 8'b10101010
    <进制><数字>默认位宽至少32位
    <数字>默认十进制
    b2 o8 d10 h16
    负数在位宽前加负号-8'b10101010
    每个字母用8位ASCII码值表示
  2. 变量
    首先说明,电路中存在的信号就是reg和wire
    ①wire型变量,网络数据类型,表示结构实体(例如门)之间的物理连接,不能储存值,而且必须受到驱动器(例如门或者连续赋值语句assign)的驱动。如果没有驱动器连到网络型数据类型变量上,该变量就为高阻z。
    Verilog程序模块中输入输出信号默认为wire型,wire可以作为任何方程式的输入,也可以用作assign语句或者实例元件的输出
    ②reg型变量,对应寄存器单元,通常用来表示always模块内的指定信号。对reg型变量赋值就像改变了一组触发器的存储单元的值。
    需要注意reg型数据可以赋负值,但当reg型数据是一个表达式中的操作数时,它被当作无符号数,也就是正值。
    always中的每一个信号必须被定义成reg型。
    数据的正负
    在电路中所有的数都是一个二进制数,有没有符号,只是我们去解读它的一个视角,而不是这个数本身的属性。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    reg [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//同理 都以补码存储
    ③integer型变量,reg和wire才是标准电路里用到的,integer可以做循环变量(虽然reg型变量也可以做循环变量),这样可以更好的区分电路里的寄存器和循环变量。
    Verilog被综合成电路,是n份循环体串联或并联起来,循环变量在电路中就被展开了,是实际上不存在的。
    注意: 课上题目以及后面写CPU不要轻易尝试automatic递归,不可综合,甚至就不要用Verilog去写递归,用硬件语言是去描述硬件的。
  3. 赋初值问题写Verilog,永远用reset来给寄存器赋初值,如果不是特别必要说明,initial都不是必要的。(除了课下的部分题目因为年代久远,可能某些测试点忘记复位信号)

运算

  1. 逻辑右移>>,算数右移>>>,合理适当使用$signed()以及位拼接运算符{}
  2. 非阻塞赋值 b <= a
    1
    在编写可综合程序时,这是最常用的方法,always建模时序逻辑一般都使用非阻塞赋值
  3. 阻塞赋值 b = a
    1

条件语句

1
2
3
4
5
6
7
8
begin
if ()
begin
end
else
begin
end
end

条件语句必须在过程块语句中使用(initial/always),并且在条件语句中也要合理使用begin-else,避免不必要的错误

case语句

1
2
3
4
5
case(rega)
`state1: ;
//...
default: ;//将所有情况列全,避免锁存器的产生
endcase //勿忘!

循环语句

1
2
3
4
5
6
//初始化memory
begin
reg[7:0] tempi;//可以用reg型或者integer作循环变量
for(tempi = 0;tempi < memsize;tempi = tempi + 1)//注意:Verilog中没有++操作
memory[tempi] = 0;
end

其他

  1. Verilog中控制时序和语句执行顺序的三种方式:
    ①基于延迟的时序控制
    ②基于事件的时序控制
    ③电平敏感的时序控制
  2. 我们一般使用的begin-end块是顺序块,也可以给块起名
    1
    2
    3
    begin:  BLOCK_NAME
    //......
    end
  3. 把输出变量声明为寄存器类型(maybe用的上)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    module 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

  1. 注意模块命名
  2. 利用A[m:n]读取相应的位

ALU

注意$signed()和三目运算符的使用

1
2
3
4
5
6
7
assign C = (ALUOp == 3'b000) ? A + B :
(ALUOp == 3'b001) ? A - B :
(ALUOp == 3'b010) ? A & B :
(ALUOp == 3'b011) ? A | B :
(ALUOp == 3'b100) ? A >> B :
(ALUOp == 3'b101) ? $signed($signed(A) >>> B)://合理使用$signed()
32'b0;

EXT

  1. 注意操作数和功能序号的区别
  2. $signed() 逻辑右移 算数右移
    1
    2
    3
    4
    5
    assign 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
always @(posedge Clk) begin
if (Reset) begin
state <= `S0;
isOver = 1'b0;
end
else if (En) begin
case (state)
`S0 : state <= `S1;
`S1 : state <= `S2;
`S2 : state <= `S3;
`S3 : state <= `S4;
`S4 : state <= `S5;
`S5 : state <= `S6;
`S6 : state <= `S7;
`S7 : begin
state <= `S0;
isOver = 1'b1;
end
default: ;
endcase
end
end

assign Overflow = isOver ? 1'b1 : 1'b0;
assign Output = state;

Verilog FSM

  1. 逻辑思考,状态编码,画出状态转移图
  2. `define or parameter表示状态
  3. 根据状态转移图构建次态逻辑与输出逻辑
  4. 写代码:
    ①同步复位or异步复位,复位一定要回到最原始状态
    ②合理使用if-else和case语句(case语句后一定要加上default语句,避免锁存器产生)
  5. 注意输出是Moore还是Mealy,可以根据波形是完整一个周期的置高还是立即置高来判断

expr

  1. Moore机且异步复位
  2. 注意逻辑思考,其实可以化成两种输入(数字or符号),四种状态(nothing or F or (F + <符号>)or 不可能再成为F)其中F遇到数字、(F + <符号>)遇到符号、nothing遇到符号,都是不可挽救状态

BlockChecker

  1. 首先是想清楚有几种输入,几种状态
  2. 这里使用栈存储begin,需要注意的是撤销end或者begin的操作(这里是对end的操作有误)
    自己可以多试几种数据比如,end begin endc ,begin end beginc 这样的
    本地要多做测试
  3. 其他错误:reset没有完全初始化、define中内容打错

易错点

  1. 引用宏定义记得加`
  2. 状态转移搞对,不要马虎,想清楚需要什么状态,怎么转移
  3. begin-end对应好,可以打一个begin打一个end,再填写中间内容,以及墙裂建议使用VScode

VoterPlus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module vote7(
input [6:0] vote,
output reg pass
);
reg[2:0] sum; // sum为reg型变量,用于统计赞成的人数
integer i; // 循环变量
always @(vote) begin // 此处使用always建模组合逻辑
sum = 3'b000; // sum初值为0
for (i = 0;i < 7;i = i + 1) begin // for语句
if (vote[i]) sum = sum + 1; // 只要有人投赞成票,则sum加1
end
if (sum >= 3'd4) pass = 1'b1; // 若大于等于4人赞成,则表决通过
else pass = 1'b0;
end
endmodule

注意:在时序逻辑中不可以对同一个变量用for循环多次非阻塞赋值,最终其实只有效赋值一次,合理的解决办法:
①利用时序逻辑改变状态,存储状态,不存在重复赋值(例如逐位清零、置1等)
②利用组合逻辑可以重复对一个变量赋值,时刻变化。

祝帆帆P1上机顺利!加油!

P1_review

首先恭喜顺利通过P1!

奇偶校验

这个与往年题一样,如果符合就最高位原样输出,如果不符合就把最高位取反。

字符串检查

不要想多了,就是一道Moore机题,“111110”状态disc置高,“1111110”状态flag置高,“1111111+”状态err置高
如果你写成mealy机会发现只置高半个周期,显然不符合题意,应该是Moore机

旅鼠

  1. 就是hdlbits上的lemmings3,感觉不难,按状态分析就好,课下交hdlbits也过了(可能课上的if都没写最后的else?要避免锁存器的产生)
  2. 写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.