Conditional ternary operator

posedge

posedge 是 Verilog 中用于检测信号上升沿的关键字,表示信号从低电平变为高电平的瞬间。

非阻塞赋值

非阻塞赋值的特性

  1. 执行机制

    • 并发执行:非阻塞赋值在当前时间步的所有语句计算完成后,才统一更新变量值。
    • 延迟生效:赋值语句不会立即改变变量值,而是 “计划” 在当前时间步结束时更新。
  2. 代码示例

    always @(posedge clk) begin
     a <= b;  // 计划在时间步结束时将b的值赋给a
     b <= a;  // 计划在时间步结束时将a的原始值赋给b
    end

    上述代码中,ab会在时钟上升沿同时更新值,因此交换了彼此的数据。

特性非阻塞赋值 (<=)阻塞赋值 (=)
执行顺序并发执行,时间步结束时统一更新顺序执行,立即更新变量值
适用场景时序逻辑(触发器、状态机)组合逻辑(算术运算、条件判断)
潜在问题无竞争条件时序逻辑中可能引发竞争和毛刺
典型应用always @(posedge clk)always @(*) 或 assign

assign

assign语句是用于描述组合逻辑的基本方式,它会持续监控右侧表达式的变化,并立即将结果赋值给左侧的信号。这种赋值方式对应实际硬件中的逻辑门电路,适合实现组合逻辑功能。

Solution

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

    // assign intermediate_result1 = compare? true: false;

    wire [7:0] m1 = a < b ? a : b;  
    wire [7:0] m2 = m1 < c ? m1 : c;
    wire [7:0] m3 = m2 < d ? m2 : d;
   
    assign min = m3;
    
endmodule

Reduction operators

The _reduction_ operators (归约运算符) can do AND, OR, and XOR of the bits of a vector, producing one bit of output:

& 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]

Solution

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

    assign parity = ^ in;
endmodule

Reduction: Even wider gates

Solution

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

Solution (Standard)

module top_module (
    input [99:0] in,
    output reg [99:0] out
);
    
    always @(*) begin
        for (int i=0;i<$bits(out);i++)        
        // $bits() is a system function that returns the width of a signal.
            out[i] = in[$bits(out)-i-1];    
            // $bits(out) is 100 because out is 100 bits wide.
    end
    
endmodule

Solution (2)

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

// for语句会占用大量资源,因此也可以采用generate for语句
genvar i;
    generate    
        for(i=0;i<100;i=i+1)begin:reverse_bit  
            assign out[99-i] = in[i];
        end
    endgenerate
    
endmodule

Combinational for-loop: 255-bit population count

Solution

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

    integer i;       // 声明循环变量
    always @(*) begin 
        out = 8'd0;  // 初始化计数器
        for(i = 0; i < $bits(in);i++) begin
            if(in[i] == 1'b1)
                out = out + 1;
        end
    end
    
endmodule

Generate for-loop: 100-bit binary adder 2

Solution

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

    // 使用generate块来实例化100个全加器
    genvar i;
    generate
        for (i = 0; i < 100; i = i + 1) begin: full_adder
            // 每个全加器的进位连接
            wire current_cin;
            
            if (i == 0) begin
                assign current_cin = cin;
            end else begin
                assign current_cin = cout[i-1];
            end
            
            // 全加器逻辑
            assign sum[i] = a[i] ^ b[i] ^ current_cin;
            assign cout[i] = (a[i] & b[i]) | (a[i] & current_cin) | (b[i] & current_cin);
        end
    endgenerate

endmodule
  1. 在 Verilog 中实例化多个相同结构需要使用 generate 语句。
  2. genvar 是一种特殊的变量类型,专门用于 生成块(generate 块) 中的循环索引。它的主要作用是在编译时展开循环,生成多个相同结构的硬件实例,而不是在运行时执行循环。

    1. 编译时展开
      genvar 声明的变量仅在编译时存在,用于控制 generate 块的循环次数。综合工具会将循环完全展开为多个独立的硬件实例,而非运行时的循环逻辑。
    2. 与普通变量的区别

      • 普通变量(如 integer):用于运行时的计算和控制流。
      • genvar:仅用于编译时生成硬件结构,循环展开后变量本身不会存在于实际硬件中。
    3. 作用域
      genvar 只能在 generate 块内部使用,且必须在循环开始前声明。

Generate for-loop: 100-digit BCD adder

Solution

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

    genvar i;
    generate 
        for (i = 0; i < 100; i = i + 1) begin: bcd_stage
            wire [3:0] a_slice = a[4*i + 3: 4*i]; //注意:4*i 中间的乘号不能省略
            wire [3:0] b_slice = b[4*i + 3: 4*i]; //注意:是冒号而不是逗号
            wire current_cin = (i == 0) ? cin : bcd_stage[i - 1].current_cout;
            
            // 实例化BCD全加器
            bcd_fadd u1 (
                .a(a_slice), //注意是用逗号分隔而不是用分号
                .b(b_slice),
                .cin(current_cin),
                .cout(current_cout),
                .sum(sum[4*i + 3: 4*i]) );
          
           // 暴露进位输出,供下一阶段使用
            wire current_cout ;  
            assign bcd_stage[i].current_cout = current_cout;  //注意:字母别抄错了
        end
    endgenerate

    // 最后一个阶段的进位输出作为模块的cout
    assign cout = bcd_stage[99].current_cout;
    
endmodule