HDLBits 刷题总结

前言

HDLBits 上的题目简单刷了一遍,自己验证过答案的源码已经汇总至 Github ,这篇博客把主要知识点再梳理一遍。
给这个Verilog的在线刷题网站给出好评,简介易用、层层铺垫,循序渐进,对于Verilog基础语法和数字逻辑的学习有很大帮助。

1. Getting Started

该网站旨在数字逻辑的教学,使用硬件描述语言Verilog HDL进行编码,提供编译(逻辑综合)和仿真环境。

本节作为开始章节,是两道非常简单的题目,使用assign进行置一和置零。

介绍了两种Verilog不同标准的端口声明形式,两者等价。

module top_module ( zero );
    output zero;
    // Verilog-1995
endmodule
module top_module ( output zero ); 

    // Verilog-2001
endmodule

2. Verilog Language

2.1 Basic

本节有8个小题,十分容易。

涉及知识点:

  1. wires 是有方向的(directional);
  2. assign
    1. 右侧向左侧赋值;(assign left_side = right_side; )
    2. 连续赋值 (continuous),即使右侧未改变;
    3. assign 出现的顺序无关;
    4. 区分按位与(&)和逻辑与(&&);
  3. 常见门电路及其运算符。

2.2 Vectors

本节有9个小题,十分容易。

涉及知识点:

  1. 位宽声明,type [upper:lower] vector_name;
  2. 切片操作,part-selected,参考Vecrot1Vector2;
  3. 拼接运算符 “{ }“,{a,b,c} 将相关信号a、b、c组合起来便于操作;
  4. 大端小端声明时确定,assign out[7:0] = in[0:7]; 的反向是无效的;
  5. 复制运算符,{num{vector}}; 将vector重复num次;

2.3 Modules: Hierarchy

本节有9个小题,介绍模块及层次结构,十分容易。

涉及知识点:

  1. 模块的概念和声明;
  2. 模块的调用(例化):按端口顺序调用、按端口名调用;
  3. 简单的加法器:
    1. 脉动进位加法器( ripple carry adder );
    2. 进位选择加法器( carry-select adder );
    3. 加减器( Adder–subtractor ).

2.4 Procedures

本节有8个小题,介绍常见过程控制语句,程序块内可以使用if-else,case相关语句。

涉及知识点:

  1. always 程序块
    1. Combinational: always @(*) 和 assign 等效。
      1. assign 左侧必须是 net 类型(如wire);
      2. always 内,左侧必须是 variable 类型(如reg);
      3. wire、reg 和硬件综合无关,只是Verilog语法要求。
    2. Clocked: always @(posedge clk)
  2. 三种赋值
    1. 连续赋值(continuous,assign x = y;),非程序块内;
    2. 阻塞赋值(blocking,x = y;),Combinational always内;
    3. 非阻塞赋值(non-blocking,x <= y;),Clocked always内。
  3. if-else
    1. 通常产生 2-1 多路复用器(2-to-1 multiplexe);
    2. 三元运算符,assign out = (condition) ? x : y;
  4. 锁存器(Latches),错误综合的代码产生(combinational logic + flip-flops),导致”需要保持输出不改变“,意味着”需要记住当前状态“,所以产生了latches。
  5. case
    1. 相当于一系列 if-elseif-else
    2. case,casex,casez 相关使用
    3. 避免产生latches,对于所有可能的情况都必须对输出赋值
      1. 在case前对所有可能的情况赋值
      2. 使用default

2.5 More Verilog Features

本节有7个小题,介绍了更多Verilog特性。

涉及知识点:

  1. 三元运算符,(condition ? if_true : if_false)
  2. 缩减运算符,是一个单元运算符,相当于按位与、按位或、按位异或等:
    1. & a[3:0] // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4’hf)
    2. | b[3:0] // OR: b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4’h0)
    3. ^ c[2:0] // XOR: c[2]^c[1]^c[0]
  3. 循环控制,for-loop;
  4. 生成块,generate for-loop。

3. Circuits

3.1 Combinational Logic

3.1.1 Basic Gates

本节有17个小题,主要介绍了基本门电路。

涉及知识点:

  1. 基本门电路:与、或、异或、与非、或非、异或非等;
  2. 根据真值表编码;
  3. 根据基本门电路和模块示意图编码;
    1. thinking “The motor is on when ___”, rather than “If (vibrate mode) then ___”.
  4. 使用拼接和切片简化代码。

3.1.2 Multiplexers

本节有5个小题,主要介绍多路复用器的实现。

涉及知识点:

  1. 2-1多路复用,使用if-else或三元运算符实现;
  2. 9-1多路复用,使用case实现;
  3. 256-1多路复用,使用下表索引实现,[7:0] sel, in[sel]是可行的;
  4. 256-1 4位多路复用,切片的索引不能同时为变量,使用下列特性实现
reg [31:0] dword;
reg [7:0] byte0;
reg [7:0] byte1;
reg [7:0] byte2;
reg [7:0] byte3;
assign byte0 = dword[0 +: 8];    // Same as dword[7:0]
assign byte1 = dword[8 +: 8];    // Same as dword[15:8]
assign byte2 = dword[16 +: 8];   // Same as dword[23:16]
assign byte3 = dword[24 +: 8];   // Same as dword[31:24]
dword[8*sel +: 8] // variable part-select with fixed width

3.1.3 Arithmetic Circuits

本节有7个小题,主要介绍算术逻辑电路,包括半加器、全加器。

涉及知识点:

  1. 有符号数;
  2. 补码,原码取反加1;
  3. 溢出,加数与被加数符号位相同,但与和数符号位不同;
  4. 4位BCD码,每4位二进制表示一个十进制数。

3.1.4 Karnaugh Map to Circuit

本节有8个小题,主要介绍卡诺图。

涉及知识点:

  1. 卡诺图的化简
  2. 与-或表达式、或-与表达式

3.2 Sequential Logic

3.2.1 Latches and Flip-Flops

本节有18个小题,介绍锁存器和触发器。

涉及知识点:

  1. 锁存器和触发器:
    1. 锁存器(Latches),电平敏感,时序逻辑,非阻塞复制
    2. 触发器(Flip-Flops),边沿敏感,时序逻辑,非阻塞复制
  2. 无法同时在敏感列表中设置时钟的上升沿和下降沿;
    1. 用两个always分别检测,然后使用2-1复用器;

3.2.2 Counters

本节有8个小题,介绍计数器。本小节开始,出现中等规模电路的题目,进行思路介绍。

涉及知识点:

  1. 4位二进制计数器,0~15,自动归零,无需判定;
  2. 4位十进制计数器,0~9,需判定归零;同理,1~12计数;
  3. 1000计数器,可以使用3个带有使能信号的0-9十进制计数器实现;
例题介绍:Count clock

Create a set of counters suitable for use as a 12-hour clock (with am/pm indicator). Your counters are clocked by a fast-running clk, with a pulse on ena whenever your clock should increment (i.e., once per second).

reset resets the clock to 12:00 AM. pm is 0 for AM and 1 for PM. hh, mm, and ss are two BCD (Binary-Coded Decimal) digits each for hours (01-12), minutes (00-59), and seconds (00-59). Reset has higher priority than enable, and can occur even when not enabled.

The following timing diagram shows the rollover behaviour from 11:59:59 AM to 12:00:00 PM and the synchronous reset and enable behaviour.

solution:

题解:时钟为1~12计数器;分钟和秒钟分别为0~60,分解为0~5和0~9计数器。分别带有使能信号,通过判断边界条件确定使能条件,使用模块例化实现完整功能。

module top_module(
    input clk,
    input reset,
    input ena,
    output pm,
    output [7:0] hh,
    output [7:0] mm,
    output [7:0] ss); 
	
    //clock enable signal
    wire SecL_EN,SecH_EN,MinL_EN,MinH_EN,Hour_EN;
    assign SecL_EN = ena;
    assign SecH_EN = ena && (ss[3:0] == 4'h9);
    assign MinL_EN = ss == 8'h59;
    assign MinH_EN = (mm[3:0] == 4'h9)&&(ss == 8'h59);
    assign Hour_EN = (mm == 8'h59)&&(ss == 8'h59);
    
    //clock
    counter10	SecL	(clk,reset,SecL_EN,ss[3:0]);
    counter6	SecH	(clk,reset,SecH_EN,ss[7:4]);
    counter10	MinL	(clk,reset,MinL_EN,mm[3:0]);
    counter6	MinH	(clk,reset,MinH_EN,mm[7:4]);
    counter12	HourHL	(clk,reset,Hour_EN,hh[7:4],hh[3:0],pm);
   
endmodule

module counter10(
    input clk,
    input reset,
    input en,
    output [3:0] Q);

    always@(posedge clk)
    begin
        if(reset)				Q <= 4'b0000;
        else if(~en)			Q <= Q;
        else if(Q == 4'b1001)	Q <= 4'b0000;
        else					Q <= Q+1'b1;
    end
endmodule 

module counter6(
    input clk,
    input reset,
    input en,
    output [3:0] Q);

    always@(posedge clk)
    begin
        if(reset)				Q<=4'b0000;
        else if(~en)			Q<=Q;
        else if(Q == 4'b0101)	Q<=4'b0000;
        else					Q<=Q+1'b1;
    end
endmodule 


module counter12(
    input clk,
    input reset,
    input en,
    output [3:0] CntH,
    output [3:0] CntL,
	output pm);

    always@(posedge clk)
    begin
        if(reset) 
            begin {CntH,CntL}<=8'h12;pm <= 1'b0; end
        else if(~en)	
            {CntH,CntL}<={CntH,CntL};
        else if ( {CntH,CntL}==8'h12 )
            begin {CntH,CntL}<=8'h01; end
        else if(CntL ==9)
            begin CntH<=CntH+1'b1; CntL<=4'b0000;end
        else if ({CntH,CntL}==8'h11 )
            begin CntH<=CntH; CntL<=CntL+1'b1;pm <= ~pm; end
        else 
            begin CntH<=CntH; CntL<=CntL+1'b1;end
    end
    
endmodule 

3.2.3 Shift Register

本节有9个小题,介绍移位寄存器。

涉及知识点:

  1. 算术移位:
    1. 算术左移:左移,符号位不变,低位补0;
    2. 算术右移:右移,符号位不变,高位补符号位;
  2. 逻辑移位:
    1. 逻辑左移:高位移出,低位补0;
    2. 逻辑右移:低位移出,高位补0;
  3. 循环移位:
    1. 循环左移:高位移出,补入低位;
    2. 循环右移:低位移出,补入高位;

3.2.4 More Circuits

该小节详见 几道有趣的HDL题目(from HDLBits)

3.2.5 Finite State Machines

该小节有33道题目,主要介绍有限状态机。

这部分虽然题量比较大,掌握三段式状态机的典型写法后,需要注意具体问题的状态转换条件。Moore和Mealy可以互相转换,一般推荐Moore,组合逻辑输出时不用考虑输入,做法是将Mealy有不同输出的单一状态分为两个状态。

涉及知识点:

  1. 分类:
    1. Moore,输出只和当前状态有关
    2. Mealy,输出与当前状态以及输入有关
  2. 三段式状态机(状态转化、状态更新、状态输出);
  3. 独热码状态机;
  4. 根据状态转换图、状态表,设计有限状态机。

部分挑选的习题详解见 Finite State Machines (from HDLBits)

3.3 Building Large Circuits

该小节详见 Building Larger Circuits ( from HDLBits )

4. Verification: Reading Simulations

4.1 Finding bugs in code

本节有5个小题,主要考察修复bug的能力,相对容易。

这些容易出现的bug包括:

  1. 将位宽不同的信号进行按位操作;
  2. 模块未按端口顺序调用;
  3. 模块例化互连错误;
  4. if-else缺失else;
  5. case没有匹配值;
  6. case前的预先赋值和default。

4.2 Build a circuit from a simulation waveform

本节有10个小题,主要考察根据时序图编写代码的能力,相对容易。

5. Verification: Weiting Testbenches

本节有5个小题,主要介绍testbenches。

发表评论

邮箱地址不会被公开。 必填项已用*标注