HDLBits_Verilog学习笔记(to be continued)

HDLBits_Verilog学习笔记(to be continued)


文章目录

    • Verilog Language
      • Procedures
        • Alwaysblock1
          • 练习
        • Alwaysblock2
          • 阻塞 VS 非阻塞 赋值语句
          • 练习
        • Always if
          • 练习
        • Always if2
          • 普遍性错误:怎么避免锁存器?
          • 演示
        • Always case
          • 练习
        • ALways case2
        • Always cacez
          • 练习
        • Always nolactches
          • 练习
        • Conditonal ternary operator
          • 练习
        • Reduction
          • 练习
        • Reduction :Even wider gates
          • 练习
        • Combinational for-loop:Vector reversal 2
          • 练习
        • Combinational for-loop 255-bit population count
        • Generate for-loop: 100-bit binary adder 2
          • 练习
        • Generate for-loop:100-digit BCD adder
          • 练习
    • Circuits
      • Combiantional Logic
        • Basic Gates
          • Wire
          • 练习
          • GND
          • 练习
          • NOR
          • 练习
          • Another gate
          • 练习
          • Two gates
          • 练习
          • More logic gates
          • 练习
          • 7420 chip
          • 练习
          • Truth tables
            • 练习
          • Two-bit equality
          • 练习
          • Simple circuit A
          • 练习
          • Simple circuit B
          • 练习
            • Combine circuit A and B
          • 练习
          • Ring or Vibrate
          • 练习
          • Thermostat
          • 练习
          • 3-bit population count
          • 练习
          • Gates and vectors
          • 练习
          • Even longer vectors
          • 练习
        • Multiplexers
          • 2-to-1 multiplexer
          • 练习
          • 2-to-1 bus multiplexer
          • 练习
          • 9-to-1 multiplexer
          • 256-to-1 multiplexer
          • 练习
          • 256-to-1 4-bit multiplexer
          • 练习
          • 256-to-1 4-bit multiplexer
          • 练习


There are some HDLBits practices.


Verilog Language

Procedures

Alwaysblock1


由于数字电路由导线连接的逻辑门组成,任何电路都可以表示为一些模块的组合和赋值语句。然而,有时这并不是描述电路最方便的方式。过程(always块就是一个例子)提供了描述电路的另一种语法。
对于硬件综合,两种类型的always块是相关的:

  • 组合电路:always @(*)
  • 时序电路:always @(posedge clk)

组合always块等价于赋值语句,因此总有一种方法可以用这两种方式来表示组合电路。选择使用哪一种主要是哪一种语法更方便的问题。注意:过程块内部的代码语法与外部的代码不同 。过程块有更丰富的语句集(例如,if-then, case),不能包含连续赋值,但也引入了许多新的非直观的出错方式。(程序性连续作业确实存在,但与连续作业有所不同)。
例如,赋值和组合always块描述同一电路。两者都创造了相同的组合逻辑。当任何一个输入(右边)改变值时,两者都会重新计算输出。

	assgin out1 = a & b | c^ d;
	always @(*)
		out2 = a & b | c ^ d;

对于组合的always块,始终使用(*)的敏感信号列表。显式地列出信号很容易出错(如果漏掉一个),并且在硬件综合时被忽略。如果你显式地指定了灵敏度列表,但漏了一个信号,综合硬件仍然会像(*)被指定一样工作,但是仿真模拟将不会也不匹配硬件的行为。(在SystemVerilog中,使用always_comb)。
关于wire和reg的注释: assign语句的左侧必须是net类型(例如wire),而过程赋值语句(在always块中)的左侧必须是变量类型(例如reg)。这些类型(wire vs. reg)与所综合的硬件没有任何关系,只是Verilog作为硬件模拟语言使用时留下的语法。

练习

使用赋值语句和组合的always块构建AND门。(由于赋值语句和组合块总是相同的功能,没有办法强制你使用这两个方法。但你是来练习的,对吧?(让你都是实现一遍))。

// synthesis verilog_input_version verilog_2001
module top_module(
    input a, 
    input b,
    output wire out_assign,
    output reg out_alwaysblock
);
		assign out_assign = a & b;
    always @(*)
        begin
            out_alwaysblock = a & b;
        end
endmodule

assign,连续赋值,就是无条件全等,对象为wire类型;alyways 就是敏感赋值,有条件相等,对象为reg,敏感信号列表,电平触发时,综合为组合电路,边沿触发时,才综合为时序电路。

Alwaysblock2


对于综合电路,两种类型always模块总是相关联的:


  • 组合电路:always @(*)
  • 时序电路:always @(posedge clk)

时序always块创建一团组合逻辑,就像组合always块一样,但也在一团组合逻辑的输出上创建一组触发器(或“寄存器”)。一团逻辑块的输出不能立即可见,而是只在下一个(posedge clk)之后的输出可见。(a blob of :一系列,团,块;不能详细定义形容的整体)。

阻塞 VS 非阻塞 赋值语句

Verilog中存在的三种赋值语句:


  • 连续赋值(assign x = y;):只能用在always块外
  • 阻塞性赋值(x = y;):用于(initial or always)程序语句内部,在赋值时,先计算等号右手部分的值,再赋值给左边变量,直到该语句赋值完成,后面的语句才能执行,会阻塞后面的语句。(顺序执行)
  • 非阻塞性赋值(x <= y;): 用于(initial or always)程序语句内部,执行赋值语句右边,然后将begin-end之间的所有赋值语句同时赋值到赋值语句的左边,但是左边的变量的值不会立即更新,直到always块所有语句执行完,才将左边变量的值更新。

注意


  • 在描述组合逻辑的always 块中用阻塞赋值,则综合成组合逻辑的电路结构。
  • 在描述时序逻辑的always 块中用非阻塞赋值,则综合成时序逻辑的电路结构。

在一个组合always块中,使用阻塞赋值。在时序always块中,使用非阻塞赋值。充分理解Verilog模拟器如何跟踪事件对硬件设计是特别有用的。如果不遵循这一规则,将很难找到仿真和合成硬件之间的不确定性和不同的错误。

练习

用三种方法构建异或门,使用赋值语句、组合always块和时序always块。请注意,时钟always块产生一个不同于其他两个的电路:有一个触发器,所以输出延迟。

// synthesis verilog_input_version verilog_2001
module top_module(
    input clk,
    input a,
    input b,
    output wire out_assign,
    output reg out_always_comb,
    output reg out_always_ff  );
    
	assign out_assign = a ^ b;
    
    always @(*)
        begin
            out_always_comb = a ^ b;
        end
    
    always @(posedge clk )
        begin 
            out_always_ff <= a ^ b;
        end
endmodule

Always if


if条件语句相当于一个二选一选择器,eg:

always @(*) begin
    if (condition) begin
        out = x;
    end
    else begin
        out = y;
    end
end

如果条件为真输出if下的,为假输出其他的;这相当于条件预算符下的连续赋值:

	assign out = (condition) ? x : y;

然而,过程赋值if条件语句中有新的一种错误,只有输出有赋值的时候才能综合成组合电路。

练习

构建一个2-to-1多路选择器,在a和b之间进行选择。如果sel_b1和sel_b2都为true,则选择b。否则,选择a。重复同样的操作两次,一次使用assign语句,一次使用过程性if语句。

module top_module(
    input a,
    input b,
    input sel_b1,
    input sel_b2,
    output wire out_assign,
    output reg out_always   ); 
    
    assign out_assign = (sel_b1 & sel_b2)? b : a;
    
    always @(*)
        begin
            if (sel_b1 == 1'b1 & sel_b2 == 1'b1)
            begin 
                out_always = b;
            end
    	else
            begin
                out_always = a;
            end
        end
endmodule

Always if2


普遍性错误:怎么避免锁存器?

在设计电路时,你必须首先从电路的角度考虑:

  • 我想设计一个逻辑门
  • 我想设计一系列组合逻辑的输入输出
  • 我想设计一系列通过许多触发器的组合逻辑输入输出

你不能做的是先写代码,然后希望它生成一个合适的电路。

  • If (cpu_overheated) then shut_off_computer = 1;
  • If (~arrived) then keep_driving = ~gas_tank_empty;

语法正确的代码不一定会产生合理的电路(组合逻辑+触发器)。通常的理由是:“在你指定的那些情况之外会发生什么?”Verilog的回答是:保持输出不变。(???问号脸)这种“保持输出不变”的行为意味着需要记住当前的状态,从而产生一个锁存器。组合逻辑(例如,逻辑门)不能记住任何状态。小心警告(10240):除非锁存器是故意的,否则它几乎总是表明有故障。组合电路必须为所有条件下的所有输出分配一个值。这通常意味着您总是需要为输出分配可选择子句或默认值。

演示

以下代码包含创建锁存器的不正确行为。修复bug,只有当电脑过热时你才会关掉它,当你到达目的地或需要加油时就停止开车。

module top_module (
    input      cpu_overheated,
    output reg shut_off_computer,
    input      arrived,
    input      gas_tank_empty,
    output reg keep_driving  ); //

    always @(*) begin
        if (cpu_overheated)
            begin
           shut_off_computer = 1;
            end //give every selection the begin ~ end ,that nice coding style
        else
            begin
                shut_off_computer = 0;
                shut_off_computer = shut_off_computer;
            end
        
    end

    always @(*) begin
        if (~arrived)
            begin
           keep_driving = ~gas_tank_empty;
            end
        else 
            begin
               keep_driving = ~arrived;
            end
    end

endmodule

Always case


Verilog中的Case语句几乎等价于一个if-elseif-else序列,它将一个表达式与其他表达式的列表进行比较。它的语法和功能与C语言中的switch语句不同。

  • 没有关键字:switch ,以case 开始,冒号结束
  • 每个case只能执行一个语句,如需要多个,请以begin-end包含
  • 可以有两个完全一样的case标签,但是使用第一个,C语言不允许这样做
  • 多个case用逗号隔开,执行最后一个冒号后的语句

练习

如果选择项多,case语句比if语句更方便。因此,在这个练习中,创建一个6:1的多路选择器。当sel在0到5之间时,选择相应的数据输入。否则,输出0。数据输入和输出都是4位宽。

module top_module ( 
    input [2:0] sel, 
    input [3:0] data0,
    input [3:0] data1,
    input [3:0] data2,
    input [3:0] data3,
    input [3:0] data4,
    input [3:0] data5,
    output reg [3:0] out   );//

    always@(*) begin  // This is a combinational circuit
        case(sel)
            3'b000:
                out = data0;
            3'b001:
                out = data1;
            3'b010:
                out = data2;
            3'b011:
                out = data3;
            3'b100:
                out = data4;
            3'b101:
                out = data5;
            default:
                out = 4'b0000;
        endcase
    end

endmodule

ALways case2

优先编码器是一种组合电路,当给定一个输入位向量时,输出该位向量的前1位的位置。例如,给定输入8’b10010000的8位优先级编码器将输出3’d4,因为位[4]是第一个高的位。
构建一个4位优先编码器。对于这个问题,如果没有一个输入位是高的(即,输入为零),输出为零。注意,4位数字有16种可能的组合。

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
    always @(*)
        begin
    case (in)
        	4'b0011, //3
            4'b0101, //6
            4'b0111, //7
            4'b1001, //9
            4'b1011, //11
            4'b1101, //13
            4'b1111, //15
            4'b0001: //1
            pos = 2'd0;
               // pos = 2'd1;
        	4'b0010, //2
            4'b0110, //6
            4'b1010, //10
            4'b1110: //14
                 pos = 2'd1;
                4'b1100, //12
                4'b0100: //4
                    pos = 2'd2;
                    4'b1000: //8
                    	pos = 2'd3;
        default:
            pos = 2'd0;
    endcase
        end
endmodule

Always cacez


对于八位的case,将会有256种可能,但如果case语句支持忽略位,将能减少到九种。什么是casez:字节比对时,对于Z的位忽略:

always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // in[3:1] can be anything
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;
    endcase
end

case语句时逐项循序检查,但存在一项对应多个输出,但第一项case项会有输出,后面项目不执行


  • casex和casez类似
  • 巴拉巴拉·····

练习
module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    always @(*)
        begin
            casez(in)
                8'bzzzzzzz1:
                    pos = 0;
                8'bzzzzzz1z:
                    pos = 1;
                8'bzzzzz1zz:
                    pos = 2;
                8'bzzzz1zzz:
                    pos = 3;
                8'bzzz1zzzz:
                    pos = 4;
                8'bzz1zzzzz:
                    pos = 5;
                8'bz1zzzzzz:
                    pos = 6;
                8'b1zzzzzzz:
                    pos = 7;
                default:
                    pos = 0;
            endcase
        end

endmodule

Always nolactches

假设你正在为一款游戏构建一个处理PS/2键盘扫描代码的电路。给定接收到的扫描码的最后两个字节,您需要指出是否按了键盘上的一个箭头键。这涉及到一个相当简单的映射,可以用一个case语句(或if-elseif)实现,有四种情况。

Scancode[16:0] arrow key
16’he06b left arrow
16’he072 down arrow
16’he074 right arrow
16’he075 up arrow
anything else none

你的电路有一个16位输入和四个输出。建立这个电路,识别这四个扫描码,并断言正确的输出。
为了避免创建锁存,必须在所有可能的条件下为所有输出赋值(参见always_if2)。仅仅有一个默认情况是不够的。必须为所有四种情况下的所有四种输出以及默认情况下的所有输出分配一个值。这可能涉及许多不必要的输入。一个简单的解决方法是在case语句之前给输出赋一个“默认值”:

always @(*) begin
    up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
    case (scancode)
        ... // Set to 1 as necessary.
    endcase
end

这种代码风格确保输出在所有可能的情况下都被赋值(0),除非case语句覆盖了赋值。这也意味着默认的:case项变得不必需了。

练习
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 
    always @(*)
            begin
        left = 1'b0;right = 1'b0;up = 1'b0;down = 1'b0;
    case(scancode)
            16'he06b:
            	left = 1'b1;
            16'he072:
            	down = 1'b1;
            16'he074:
            	right = 1'b1;
            16'he075:
            	up = 1'b1;
            default:
            	;//can without case ouput satement
            endcase
        end
            	
        
endmodule


Conditonal ternary operator

verilog也有和C语言类似的条件操作符:?

练习

给定四个无符号数,求最小值。无符号数可以与标准的比较运算符(a < b)进行比较。使用条件运算符构造两路最小电路,然后再组合成四路最小电路。你可能需要一些线向量来得到中间结果。

module top_module (
    input [7:0] a, b, c, d,
    output [7:0] min);//

    // assign intermediate_result1 = compare? true: false;
    assign min = (((a

Reduction

你已经熟悉了两个值之间的位运算,例如,a & b或a ^ b。有时,你想创建一个宽的门来操作一个向量的所有位,比如(a[0] & a[1] & a[2] & a[3]…),如果向量很长,这就很乏味了。
归约运算符可以对向量的位进行与运算、或运算和异或运算,产生1位输出:

& a[3:0]     // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0]     // OR:  b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0]     // XOR: c[2]^c[1]^c[0]

这些是只有一个操作数的一元操作符(类似于NOT操作符!和~)。你也可以反转这些输出来创建NAND, NOR和XNOR门,例如(~& d[7:0])。

练习

奇偶校验经常被用来作为一种简单的检测错误的方法,当通过一个不完美的通道传输数据。创建一个电路,将计算一个8位字节的奇偶校验位(这将为字节添加第9位)。我们将使用“偶”奇偶校验,其中奇偶校验位就是所有8个数据位的异或

module top_module (
    input [7:0] in,
    output parity); 
	assign parity = ^ in;
endmodule

Reduction :Even wider gates

整一个有以下三种输出的,100种输入的组合逻辑:


  • out_and:100输入的与门
  • out_or:100输入的或门
  • out_xor:100输入的异或门

归约操作符包括:归约与(&),归约与非(&),归约或(|),归约或非(|),归约异或(),归约同或(~)。归约操作符是单目操作符号,它对这个向量操作数逐位操作,最终产生一个 1bit 结果。逻辑操作符、按位操作符和归约操作符都使用相同的符号表示,因此有时候容易混淆。区分这些操作符的关键是分清操作数的数目,和计算结果的规则。

练习
	module top_module( 
    input [99:0] in,
    output out_and,
    output out_or,
    output out_xor 
);
     assign       out_and = &  in;
     assign       out_or  = |  in;
     assign       out_xor = ^  in;
        
endmodule

Combinational for-loop:Vector reversal 2

给出一个100bit的向量,然后翻转以下(用for循环解决)

练习
module top_module( 
    input [99:0] in,
    output [99:0] out
);
    always @(*)
        begin
            for(integer i = 0 ; i < 100 ; i = i + 1)
                begin
                    out [i] = in [99 - i];
                end
        end

endmodule

Combinational for-loop 255-bit population count

搞一个输入信号向量中为1的计数器,就是在循环中带条件判断

module top_module( 
    input [254:0] in,
    output [7:0] out );

    always @(*)
        begin
          out = 0;
            for(integer i = 0 ; i < 255 ; i = i + 1)
                begin 
                    if(in[i] == 1'b1)
                        begin 
                            out = out + in[i];
                        end
                    else
                        begin
                            out = out + in[i];
                        end
                end
  
        end
endmodule

Generate for-loop: 100-bit binary adder 2


通过实例化100个全加法器来创建一个100位二进制循环进位加法器。加法器将两个100位的数和一个进位数相加,得到一个100位的和并进位。为了鼓励您实际实例化全加法器,还输出循环进位加法器中每个全加法器的输出。Cout[99]是最后一个完整的加法器的最后一个执行量,也是你经常看到的执行量。

练习
module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );
    
    assign cout[0] = a[0] & b[0] | a[0] & cin | b[0] & cin; //luo ji dai shu shi zui jian
    
    assign sum[0]  = a[0] ^ b[0] ^ cin;
    
     integer i;
    
     always @ (*)
         begin
             for (i=1; i<100; i = i + 1)
             begin
                 cout[i] = a[i] & b[i] | a[i] & cout[i-1] | b[i] & cout[i-1];
                 sum[i]  = a[i] ^ b[i] ^ cout[i-1];
         end
     end

endmodule

Generate for-loop:100-digit BCD adder

提供一个bcd_fadd 的一位BCD码加法器,做两个BCD码和进位的加法,最后输出总和以及进位:

module bcd_fadd (
    input [3:0] a,
    input [3:0] b,
    input     cin,
    output   cout,
    output [3:0] sum );
    

实例化100个bcd_fadd,创建100位BCD循环进位加法器。你的加法器应该将两个100位的BCD数字被压缩成400位的向量)和一个进位相加,得到一个100位的和并进位。


  1. 若两个BCD码之和小于等于1001,不需要修正
  2. 若在10-15间,需要向高位进位,同时进行加6修正,进位是在加6时修正产生
  3. 若之和在16-18间,向高位进位会在相加过程中产生,对本位还要加6修正

练习
module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );
    
    wire [399: 0] cout_t;
    
    bcd_fadd fadd(.a(a[3:0]),.b(b[3:0]),.cin(cin),.cout(cout_t[0]),.sum(sum[3:0]));
    
    //all cout_t assign to cout,but inimadtied first,so have 396
    
    assign cout = cout_t[396];
    //generate one time, Instantiate one 
    generate
        genvar i;
        for(i = 4; i < 400; i=i+4) 
            begin : add
              bcd_fadd fadd(.a(a[i+3:i]), .b(b[i+3:i]), .cin(cout_t[i-4]), .cout(cout_t[i]),.sum(sum[i+3:i]));
             end
    endgenerate
endmodule

Circuits

Combiantional Logic

Basic Gates

Wire

实现以下电路:


in ———— out


练习
module top_module (
    input in,
    output out);
		assign out = in; //combinational logic use blocking assignment
endmodule

GND

实现以下电路:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdbeq21Z-1651335732619)(https://hdlbits.01xz.net/mw/images/5/54/Exams_m2014q4i.png)]


练习
module top_module (
    output out);
	assign out = 1'b0;
endmodule

NOR

实现下图电路:


HDLBits_Verilog学习笔记(to be continued)_第1张图片


就是实现一个或非门

练习
module top_module (
    input in1,
    input in2,
    output out);
    assign out = !(in1 || in2); //output is 1bit
    assign out = ~(in1 | in2);//output is same as input width
endmodule

注意逻辑运算符号与按位运算符号的区别

Another gate

实现下图电路:


HDLBits_Verilog学习笔记(to be continued)_第2张图片


对输入信号in2取反后的与门

练习
module top_module (
    input in1,
    input in2,
    output out);
    assign out = in1 && (!in2);
    //assign out = in1 & (~in2);

endmodule

Two gates

实现下图电路:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QLV7s8Hs-1651335732622)(https://hdlbits.01xz.net/mw/images/e/e6/Exams_m2014q4g.png)]


in1/2信号先同或,结果再和in3异或,^~^,是互补运算,Verilog中只有按位的模式

练习
module top_module (
    input in1,
    input in2,
    input in3,
    output out);
    assign out = in3 ~^ (in1 ^ in2);
endmodule
More logic gates

试着同时构建几个逻辑门。建立一个有两个输入a和b的组合电路。
有7个输出,每个输出都有一个逻辑门驱动:

  • out_and:a and b
  • out_or:a or b
  • out_xor:a or b
  • out_nand: a nand b
  • out_nor: a nor b
  • out_xnor: a xnor b
  • out_anotb: a and-nt b

练习
module top_module( 
    input a, b,
    output out_and,
    output out_or,
    output out_xor,
    output out_nand,
    output out_nor,
    output out_xnor,
    output out_anotb
);
	assign out_and = a && b;//a & b;
    assign out_or  = a || b;//a | b;
    assign out_xor = a ^ b;
    assign out_nand = !(a && b); //~(a & b);
    assign out_nor = !(a || b);//(~(a | b));
    assign out_xnor = a ~^ b;//~(a ^ b);
    assign out_anotb = a && !b;//a & (~b);
endmodule

7420 chip

7400系列集成电路是一系列数字芯片,每个芯片有几个门。7420是一个带有两个4输入NAND门的芯片。
创建一个功能与7420芯片相同的模块。它有8个输入和2个输出。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0AjK3a3a-1651335732622)(https://hdlbits.01xz.net/mw/images/4/48/7420.png)]


练习
module top_module ( 
    input p1a, p1b, p1c, p1d,
    output p1y,
    input p2a, p2b, p2c, p2d,
    output p2y );
    assign p1y = ~(p1a & p1b & p1c & p1d);
    assign p2y = !(p2a & p2b & p2c & p2d);

endmodule
Truth tables

在前面的练习中,我们使用了简单的逻辑门和几个逻辑门的组合。这些电路是组合电路的例子。组合意味着电路的输出是其输入的函数(在数学意义上)。这意味着对于任何给定的输入值,只有一个可能的输出值。因此,描述组合函数行为的一种方法是显式列出输入的每个可能值的输出。这是真值表。
对于一个有N个输入的布尔函数,有2n 种可能的输入组合。真值表的每一行列出一个输入组合,所以总有2n行。输出列显示每个输入值的输出应该是什么。

row inputs outputs
number x3 x2 x1 f
0 0 0 0 0
1 0 0 1 0
2 0 1 0 1
3 0 1 1 1
4 1 0 0 0
5 1 0 1 2
6 1 1 0 0
7 1 1 1 1

上面的真值表适用于一个三输入一输出函数。对于8种可能的输入组合,每一种都有8行,还有一个输出列。有四种输出为1的输入组合,以及四种输出为0的输入组合。


根据真实表综合电路
假设我们想要构建上面的电路,但是我们被限制只能使用一组标准逻辑门。如何构建任意逻辑函数(表示为真值表)?
实现真值表功能的电路的一个简单方法是将真值表的功能表示为积和形式。乘积的和(意为或)意味着在真值表的每一行使用一个n输入与门(用来检测输入是否匹配每一行),然后是一个或门,它只选择那些输出为“1”的行。
对于上面的例子,如果输入匹配第2行或第3行或第5行或第7行,则输出为’1’(这是一个4输入or门)。如果x3=0, x2=1, x1=0,则输入匹配第2行(这是一个3输入and门)。因此,这个真值表可以通过使用4个或的与门来实现。(数字电路部分内容,不会的,单独补课吧的)


练习

创建一个实现上述真值表的组合电路。
HDLBits_Verilog学习笔记(to be continued)_第3张图片

module top_module( 
    input x3,
    input x2,
    input x1,  // three inputs
    output f   // one output
);
    assign f = ((!x3)&&x2&&(!x1))||((!x3)&&x2&&x1)||(x3&&(!x2)&&x1)||(x3&&x2&&x1);
/*
// This truth table has four minterms. 
	assign f = ( ~x3 & x2 & ~x1 ) | 
				( ~x3 & x2 & x1 ) |
				( x3 & ~x2 & x1 ) |
				( x3 & x2 & x1 ) ;
				
	// It can be simplified, by boolean algebra or Karnaugh maps.
	// assign f = (~x3 & x2) | (x3 & x1);
	
	// You may then notice that this is actually a 2-to-1 mux, selected by x3:
	// assign f = x3 ? x1 : x2;
	*/

endmodule
Two-bit equality

创建一个电路,有两个2位输入a[1:0]和B[1:0],并产生一个输出z。如果a = B, z的值应该是1,否则z应该是0

练习
module top_module ( input [1:0] A, input [1:0] B, output z ); 
    assign z = (A[1:0] == B[1:0]);
endmodule
Simple circuit A

模块A应该实现函数z = (x^y) & x。实现这个模块。

练习
module top_module (input x, input y, output z);
    assign z = (x ^ y) & x;
endmodule
Simple circuit B

电路B可以用如下仿真波形来描述:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-muEO5lvW-1651335732624)(https://hdlbits.01xz.net/mw/thumb.php?f=Mt2015_q4b.png&width=1200)]
由波形图可知,xy相同为真,想以为假,所以为同或


练习
module top_module ( input x, input y, output z );
assign z = x ~^ y;
endmodule

Combine circuit A and B

参考mt2015_q4qmt2015_q4b,顶层设计包括子电路A和子电路B的两个实例化,如下所示。
HDLBits_Verilog学习笔记(to be continued)_第4张图片


mt2015_q4a:z = (x^y)&x
mt2015_q4b:z=~(x ^ y)


练习
module top_module (input x, input y, output z);
    assign z = (((x^y)&x)&(x ~^ y))^(((x^y)&x)|(x ~^ y));
    // Or you could simplify the circuit including the sub-modules:
	// assign z = x|~y;
endmodule
Ring or Vibrate

假设你正在设计一个控制手机铃声和振动电机的电路。无论何时电话需要来电铃声(输入铃声),您的电路必须打开振铃(输出铃声= 1)或电机(输出电机= 1),但不能同时打开。如果电话处于振动模式(输入vibrate_mode = 1),打开电机。否则,请打开铃声。

尝试只使用赋值语句,看看是否可以将问题描述转换为逻辑门的集合.

设计提示: 在设计电路时,我们常常要“逆向”思考问题,即从输出开始,然后逆向思考输入。这通常与人们考虑(顺序的、命令式的)编程问题的方式相反,在这种情况下,人们会首先查看输入,然后决定一个操作(或输出)。对于顺序程序,人们通常会想“如果(输入是_)那么(输出应该是_)”。另一方面,硬件设计师经常认为“当(输出是_时,(输入应该是_)”。

上面的问题描述是用一种命令式的形式写的,适合软件编程(如果ring然后这样做),所以你必须把它转换成更适合硬件实现的声明形式(赋值ringer = ___)。能够思考和转换这两种风格是硬件设计最重要的技能之一。
HDLBits_Verilog学习笔记(to be continued)_第5张图片


练习
module top_module (
    input ring,
    input vibrate_mode,
    output ringer,       // Make sound
    output motor         // Vibrate
);
    assign ringer = (ring ^ vibrate_mode) & ring;
    assign motor  =  (ring ~^ vibrate_mode) & vibrate_mode;
    /*
    // When should ringer be on? When (phone is ringing) and (phone is not in vibrate mode)
	assign ringer = ring & ~vibrate_mode;
	
	// When should motor be on? When (phone is ringing) and (phone is in vibrate mode)
	assign motor = ring & vibrate_mode;
    */

endmodule
Thermostat

一个加热/冷却恒温器控制加热器(在冬天)和空调(在夏天)。设计一个电路,合理打开和关闭加热器,空调,风机风机。

恒温器可处于制热(模式= 1)和制冷(模式= 0)两种模式之一。制热模式下,当温度过低(too_cold = 1)时打开加热器,但不要使用空调。在制冷模式下,如果天气太热(too_hot = 1),建议开启空调,但不要开启加热器。当加热器或空调开启时,也要打开风扇,使空气流通。此外,用户也可以要求打开风机(fan_on = 1),即使加热器和空调处于关闭状态。

尝试只使用赋值语句,看看是否可以将问题描述转换为逻辑门的集合。

预期解决方案长度:大约3行。


练习
module top_module (
    input too_cold,
    input too_hot,
    input mode,
    input fan_on,
    output heater,
    output aircon,
    output fan
); 
    //mode decide only heater or aircon is on
    assign heater = too_cold & mode;
        assign aircon = too_hot & ~mode;
            assign fan = heater | aircon | fan_on;

endmodule
3-bit population count

“流行计数”电路计算输入向量中“1”的数量。为3位输入向量建立人口计数电路。

练习
module top_module( 
    input [2:0] in,
    output [1:0] out );
   /*
    // This is a function of 3 inputs. One method is to use a 8-entry truth table:
	// in[2:0] out[1:0]
	// 000      00
	// 001      01
	// 010      01
	// 011      10
	// 100      01
	// 101      10
	// 110      10
	// 111      11
	assign out[0] = (~in[2] & ~in[1] & in[0]) | (~in[2] & in[1] & ~in[0]) | (in[2] & ~in[1] & ~in[0]) | (in[2] & in[1] & in[0]);
	assign out[1] = (in[1] & in[0]) | (in[2] & in[0]) | (in[2] & in[1]);
   */
    assign out = in[2] + in[1] + in[0];
endmodule
Gates and vectors

你得到一个4位输入向量。我们想知道每个比特和它的邻居之间的一些关系:


  • out_both:这个输出向量的每一位都应该表明相应的输入位和它左边的邻居(更高的索引)是否都是’1’。例如,out_both[2]应该表示in[2]和in[3]是否都为1。因为在[3]的左边没有邻居,所以答案很明显,所以我们不需要知道[3]。
  • out_any:这个输出向量的每一位都应该表示相应的输入位及其右边的邻居是否为“1”。例如,out_any[2]应表明在in[2]或在in[1]中是否为1。因为在in[0]的右边没有邻居,所以答案很明显,所以我们不需要知道out_any[0]。
  • out_different:这个输出向量的每一位都应该表明相应的输入位是否与其左边的邻居不同。例如,out_different[2]应该表示in[2]和in[3]是否不同。对于这一部分,把向量看做是绕来绕去的,所以[3]左边的邻居在[0]中。

练习
module top_module( 
    input [3:0] in,
    output [2:0] out_both,
    output [3:1] out_any,
    output [3:0] out_different );
    assign out_both [2:0]= (in[0] && in[1])+((in[1] && in[2])<<1)+((in[2] && in[3])<<2);
    assign out_any[3:1]  =(in[0] | in[1])+((in[1] | in[2])<<1)+((in[2] | in[3])<<2);
    assign out_different[3:0]  =(in[0] ^ in[1])+((in[1] ^ in[2])<<1)+((in[2] ^ in[3])<<2)+((in[0] ^ in[3])<<3);
    /*
    
	// Use bitwise operators and part-select to do the entire calculation in one line of code
	// in[3:1] is this vector:   					 in[3]  in[2]  in[1]
	// in[2:0] is this vector:   					 in[2]  in[1]  in[0]
	// Bitwise-OR produces a 3 bit vector.			   |      |      |
	// Assign this 3-bit result to out_any[3:1]:	o_a[3] o_a[2] o_a[1]

	// Thus, each output bit is the OR of the input bit and its neighbour to the right:
	// e.g., out_any[1] = in[1] | in[0];	
	// Notice how this works even for long vectors.
	assign out_any = in[3:1] | in[2:0];

	assign out_both = in[2:0] & in[3:1];
	
	// XOR 'in' with a vector that is 'in' rotated to the right by 1 position: {in[0], in[3:1]}
	// The rotation is accomplished by using part selects[] and the concatenation operator{}.
	assign out_different = in ^ {in[0], in[3:1]};
	
    */
endmodule
Even longer vectors

你在[99:0]中得到一个100位的输入向量。我们想知道每个比特和它的邻居之间的一些关系:


  • out_both:这个输出向量的每一位都应该表明相应的输入位和它左边的邻居是否都是’1’。例如,out_both[98]应该表明in[98]和in[99]是否都为1。因为在[99]的左边没有邻居,所以答案是显而易见的,所以我们不需要知道两个[99]。
  • out_any:这个输出向量的每一位应该表明是否有相应的输入点和其邻国向右是’ 1 '。例如,out_any[2]应表明在[2]或在[1]中是否为1。因为在[0]的右边没有邻居,所以答案很明显,所以我们不需要知道任何[0]。
  • out_different:这个输出向量的每一位都应该表明相应的输入位是否与其左边的邻居不同。例如,out_different[98]应该表示in[98]和in[99]是否不同。对于这一部分,将向量视为环绕,因此在[99]的左侧邻居在[0]中。

练习
module top_module( 
    input [99:0] in,
    output [98:0] out_both,
    output [99:1] out_any,
    output [99:0] out_different );
    assign out_both = in[98:0] & in[99:1];
    assign out_any  = in[98:0] | in[99:1];
    assign out_different = in ^ {in[0],in[99:1]};
    /*
    // See gatesv for explanations.
	assign out_both = in & in[99:1];
	assign out_any = in[99:1] | in ;
	assign out_different = in ^ {in[0], in[99:1]};
    */

endmodule

Multiplexers

2-to-1 multiplexer

创建一个1位宽、2对1的多路复用器。当sel=0时,选择a,当sel=1时,选择b。


预期解决长度: 一行


练习
module top_module( 
    input a, b, sel,
    output out ); 
    
    assign out = sel ? b : a;
    //assign out = (sel & b) | (~sel & a);
endmodule


2-to-1 bus multiplexer

建一个100位宽,2对1的多路复用器。当sel=0时,选择a,当sel=1时,选择b。


预期解决长度: 一行


练习
module top_module (
	input [99:0] a,
	input [99:0] b,
	input sel,
	output [99:0] out
);

	assign out = sel ? b : a;
	
	// The following doesn't work. Why?
	// assign out = (sel & b) | (~sel & a);
	
endmodule

9-to-1 multiplexer

创建一个16位宽、9对1的多路复用器。Sel =0选择a, Sel =1选择b,等等。对于未使用的情况(sel=9到15),将所有输出位设置为’1’。

预期解决长度: 1~5行


module top_module( 
    input [15:0] a, b, c, d, e, f, g, h, i,
    input [3:0] sel,
    output [15:0] out );
    always @(*)
        begin
            case(sel)
                4'b0000: out = a;
                4'b0001: out = b;
                4'b0010: out = c;
                4'b0011: out = d;
                4'b0100: out = e;
                4'b0101: out = f;
                4'b0110: out = g;
                4'b0111: out = h;
                4'b1000: out = i;
                default:
                    	out = '1;
                // '1 is a special literal syntax for a number with all bits set to 1.
						// '0, 'x, and 'z are also valid.
            endcase
        end
endmodule

256-to-1 multiplexer

创建一个1位宽,256对1的多路复用器。256个输入都被打包成一个256位输入向量。Sel =0应该选择[0]中的位,Sel =1选择[1]中的位,Sel =2选择[2]中的位,等等。

预期解决长度: 一行


练习

module top_module( 
    input [255:0] in,
    input [7:0] sel,
    output out );
    assign out = in[sel];
endmodule

256-to-1 4-bit multiplexer

创建一个4位宽,256对1的多路复用器。256个4位输入全部打包成一个1024位输入向量。Sel =0应该选择[3:0]中的比特数,Sel =1选择[7:4]中的比特数,Sel =2选择[11:8]中的比特数,等等。

预期解决长度: 一~五行


练习

module top_module( 
    input [1023:0] in,
    input [7:0] sel,
    output [3:0] out );
    assign out = in[(sel * 4)+:4];
endmodule

g;
4’b0111: out = h;
4’b1000: out = i;
default:
out = '1;
// '1 is a special literal syntax for a number with all bits set to 1.
// '0, 'x, and 'z are also valid.
endcase
end
endmodule

*************
##### 256-to-1 multiplexer
*********
创建一个1位宽,256对1的多路复用器。256个输入都被打包成一个256位输入向量。Sel =0应该选择[0]中的位,Sel =1选择[1]中的位,Sel =2选择[2]中的位,等等。

==预期解决长度:==一行
***********
##### 练习
***********
```verilog
module top_module( 
    input [255:0] in,
    input [7:0] sel,
    output out );
    assign out = in[sel];
endmodule

256-to-1 4-bit multiplexer

创建一个4位宽,256对1的多路复用器。256个4位输入全部打包成一个1024位输入向量。Sel =0应该选择[3:0]中的比特数,Sel =1选择[7:4]中的比特数,Sel =2选择[11:8]中的比特数,等等。

预期解决长度: 1~5行


练习

module top_module( 
    input [1023:0] in,
    input [7:0] sel,
    output [3:0] out );
    assign out = in[(sel * 4)+:4];
endmodule

5.1-00:21

你可能感兴趣的:(FPGA,学习,fpga开发)