HDLBits——Procedures

Posted Mount256

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDLBits——Procedures相关的知识,希望对你有一定的参考价值。

----- 29. Alwaysblock1 -----

Problem Statement

Build an AND gate using both an assign statement and a combinational always block.

Answer

// 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 @(*) out_alwaysblock = a & b;

endmodule

Note

For synthesizing hardware, two types of always blocks are relevant:

  • Combinational: always @(*)
  • Clocked: always @(posedge clk)

Note: Always Block (Combinational)

  • The syntax for code inside a procedural block is different from code that is outside. Procedural blocks have a richer set of statements (e.g., if-then, case), cannot contain continuous assignments, but also introduces many new non-intuitive ways of making errors. (Procedural continuous assignments do exist, but are somewhat different from continuous assignments, and are not synthesizable.)
  • wire vs. reg: The left-hand-side of an assign statement must be a net type (e.g., wire), while the left-hand-side of a procedural assignment (in an always block) must be a variable type (e.g., reg). (批注:always里必须是变量类型) These types (wire vs. reg) have nothing to do with what hardware is synthesized, and is just syntax left over from Verilog’s use as a hardware simulation language.

----- 30. Alwaysblock2 -----

Problem Statement

Build an XOR gate three ways, using an assign statement, a combinational always block, and a clocked always block. Note that the clocked always block produces a different circuit from the other two: There is a flip-flop so the output is delayed.

Answer

// 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 @(*) out_always_comb = a ^ b;
    always @(posedge clk) out_always_ff = a ^ b;

endmodule

Note: Always Block (Clocked)

  • Clocked always blocks create a blob of combinational logic just like combinational always blocks, but also creates a set of flip-flops(触发器) (or “registers(寄存器)”) at the output of the blob of combinational logic. Instead of the outputs of the blob of logic being visible immediately, the outputs are visible only immediately after the next (posedge clk)(上升沿时钟信号). (批注:在always里,输出并不是立刻“表现”出来,而是在下一个时钟沿出现时才会全部“表现”出来)

Note: Blocking vs. Non-Blocking Assignment

There are three types of assignments in Verilog:

  • Continuous assignments (assign x = y;). Can only be used when not inside a procedure (“always block”).
  • Procedural blocking assignment: (x = y;). Can only be used inside a procedure.(批注:x的值立即被赋值为y,完成本句阻塞赋值语句后才能执行下一非阻塞语句,并且赋值操作同时与块内其他阻塞语句同时完成)
  • Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure. (批注:块内的非阻塞赋值语句是分为两个步骤进行的,前后步骤有一个时间差(time-slot),期间不影响其他语句的执行,可参考下面的长批注
  • In a combinational always block, use blocking assignments. In a clocked always block, use non-blocking assignments.(批注:在always块中,组合逻辑电路使用阻塞赋值,时序逻辑电路使用非阻塞赋值) A full understanding of why is not particularly useful for hardware design and requires a good understanding of how Verilog simulators keep track of events. Not following this rule results in extremely hard to find errors that are both non-deterministic and differ between simulation and synthesized hardware.(批注:非阻塞赋值的Verilog的合成,编码风格:Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!

长批注:上面说的不够详细,这里重复说一下阻塞赋值和非阻塞赋值的区别:阻塞赋值操作符为“=”,非阻塞赋值操作符为“<=”。阻塞语句没有相对应的硬件电路,综合结果未知,而非阻塞语句存在对应的电路。阻塞语句是立即发生的,而非阻塞语句的执行可看做两个步骤:1)在赋值时刻开始时,计算非阻塞赋值右侧表达式(RHS);2)在赋值时刻结束时,更新非阻塞赋值左侧表达式(LHS),这两个步骤之间过去的时间是一个时间片(time-slot,可查阅了解),在这期间任何时刻,RHS随时可能会被更新(计算)多次。阻塞赋值是顺序执行的,而非阻塞赋值是并行执行的)

----- 31. Always if -----

Problem Statement

Build a 2-to-1 mux that chooses between a and b. Choose b if both sel_b1 and sel_b2 are true. Otherwise, choose a. Do the same twice, once using assign statements and once using a procedural if statement.

sel_b1sel_b2out_assign, out_always
00a
01a
10a
11b

Answer

// synthesis verilog_input_version verilog_2001
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 == 1) ? b : a;
	 
	always @(*) begin
		if(sel_b1 & sel_b2 == 1)begin
			out_always = b;
		end
		else begin
			out_always = a;
		end
	end

endmodule

Note

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

is equal to:

assign out = (condition) ? x : y;

However, the procedural if statement provides a new way to make mistakes. The circuit is combinational only if out is always assigned a value.

----- 32. Always if2 -----

Problem Statement

The following code contains incorrect behaviour that creates a latch. Fix the bugs so that you will shut off the computer only if it’s really overheated, and stop driving if you’ve arrived at your destination or you need to refuel(加油).

always @(*) begin
    if (cpu_overheated)
       shut_off_computer = 1;
end

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

Answer

// synthesis verilog_input_version verilog_2001
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)
           shut_off_computer = 1;
		else
		   shut_off_computer = 0;
    end

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

endmodule

Note

  • Combinational logic (e.g., logic gates) cannot remember any state. Watch out for “Warning (10240): … inferring latch(es)” messages. Unless the latch was intentional, it almost always indicates a bug. Combinational circuits must have a value assigned to all outputs under all conditions. This usually means you always need else clauses or a default value assigned to the outputs.

----- 33. Always case -----

Problem Statement

Create a 6-to-1 multiplexer. When sel is between 0 and 5, choose the corresponding data input. Otherwise, output 0. The data inputs and outputs are all 4 bits wide.

Be careful of inferring latches.

Answer

// synthesis verilog_input_version verilog_2001
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

Note

  • Each case item can execute exactly one statement. This makes the “break” used in C unnecessary. But this means that if you need more than one statement, you must use begin … end.
  • Duplicate (and partially overlapping(部分重复的分支项)) case items are permitted. The first one that matches is used.

----- 34. Always case2 -----

Problem Statement

A priority encoder(优先编码器) is a combinational circuit that, when given an input bit vector, outputs the position of the first 1 bit in the vector. For example, a 8-bit priority encoder given the input 8’b10010000 would output 3’d4, because bit[4] is first bit that is high. (批注:位数越低,优先级别越高)

Build a 4-bit priority encoder. For this problem, if none of the input bits are high (i.e., input is zero), output zero. Note that a 4-bit number has 16 possible combinations.

Answer

// synthesis verilog_input_version verilog_2001
module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
    
    always@(*) begin  
         case(in)
				4'b0000: pos = 2'd0;
				4'b0001: pos = 2'd0;
				4'b0010: pos = 2'd1;
				4'b0011: pos = 2'd0;
				
				4'b0100: pos = 2'd2;
				4'b0101: pos = 2'd0;
				4'b0110: pos = 2'd1;
				4'b0111: pos = 2'd0;
				
				4'b1000: pos = 2'd3;
				4'b1001: pos = 2'd0;
				4'b1010: pos = 2'd1;
				4'b1011: pos = 2'd0;
				
				4'b1100: pos = 2'd2;
				4'b1101: pos = 2'd0;
				4'b1110: pos = 2'd1;
				4'b1111: pos = 2'd0;
		  endcase
	end

endmodule

----- 35. Always casez -----

Problem Statement

Build a priority encoder for 8-bit inputs. Given an 8-bit vector, the output should report the first bit in the vector that is 1. Report zero if the input vector has no bits that are high. For example, the input 8’b10010000 should output 3’d4, because bit[4] is first bit that is high.

Answer1

// synthesis verilog_input_version verilog_2001
module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    
    always@(*) begin  
        casez(in[7:0])
				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

Answer2

module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    
    always@(*) begin  
        casez(in[7:0])
			    8'b???????1: pos = 3'd0;
				8'b??????10: pos = 3'd1;
				8'b?????100: pos = 3'd2;
				8'b????1000: pos = 3'd3;
				8'b???10000: pos = 3'd4;
				8'b??100000: pos = 3'd5;
				8'b?1000000: pos = 3'd6;
				8'b10000000: pos = 3'd7;
				default: pos = 3'd0;
		  endcase
	 end

endmodule

Note: casez

  • A case statement behaves as though each item is checked sequentially(按顺序地) (in reality, it does something more like generating a giant truth table then making gates). Notice how there are certain inputs (e.g., 4’b1111) that will match more than one case item. The first match is chosen (so 4’b1111 matches the first item, out = 0, but not any of the later ones).(批注:输入与哪个选项匹配跟选项的先后顺序有关。因此,使用这种写法的时候需要留意选项顺序)
  • The digit ? is a synonym(等同于) for z. so 2’bz0 is the same as 2’b?0
  • There is also a similar casex that treats both x and z as don’t-care. I don’t see much purpose to using it over casez.(批注:casex和casez效果是一样的,但在可综合设计中,只能用case和casez,不能用casex,因为综合时casex中的x和z都被视为don’t care,综合前后仿真结果不一致。不推荐使用casex语句,因为x状态为仿真的初值,容易引起误会。另外,建议无关项用?表示,而不是x或者z)

----- 36. Always nolatches -----

Problem Statement

Suppose you’re building a circuit to process scancodes(扫描码) from a PS/2 keyboard for a game. Given the last two bytes of scancodes received, you need to indicate whether one of the arrow keys on the keyboard have been pressed. This involves a fairly simple mapping, which can be implemented as a case statement (or if-elseif) with four cases.

Scancode [15:0]Arrow key
16’he06bleft arrow
16’he072down arrow
16’he074right arrow
16’he075up arrow
Anything elsenone

Your circuit has one 16-bit input, and four outputs. Build this circuit that recognizes these four scancodes and asserts the correct output.

To avoid creating latches, all outputs must be assigned a value in all possible conditions. Simply having a default case is not enough. You must assign a value to all four outputs in all four cases and the default case. This can involve a lot of unnecessary typing. One easy way around this is to assign a “default value” to the outputs before the case statement:

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

This style of code ensures the outputs are assigned a value (of 0) in all possible cases unless the case statement overrides the assignment. This also means that a default: case item becomes unnecessary.

Reminder: The logic synthesizer generates a combinational circuit that behaves equivalently to what the code describes. Hardware does not “execute” the lines of code in sequence.

Answer

// synthesis verilog_input_version verilog_2001
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 
    
    always @(*) begin
			up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
			case (scancode)
				16'he06b: left = 1'b1;
				16'he072: down = 1'b1;
				16'he074: right = 1'b1;
				16'he075: up = 1'b1;
			endcase
	end

endmodule

以上是关于HDLBits——Procedures的主要内容,如果未能解决你的问题,请参考以下文章

HDLBits——Vectors

HDLBits——Modules

HDLBits——Basic Gates

HDLBits——More Verilog Features

012 HDLBits

HDLBits——Karnaugh Map to Circuit