Rule 90.数组的异或规则是对应分量两两异或，得到一个新的数组。 Solutionmodule top_module( input clk, input load, input [511:0] data, output [511:0] q ); always @(posedge clk) begin if (load) //别忘了先加载 q <= data; else q <= {1'b0,q[511:1]} ^ {q[510:0],1'b0}; end endmodule Rule 110将左、中、右状态组合为 3 位索引，查表（Rule 110 的转移表）得到下一状态。将左、中、右分别作为三个变量（含有512个分量的数组），作卡诺图：L\C R000111100011110101于是，$C^{(n+1)} = \bar{C}R +\bar{L}C+C\bar{R}$ Solutionmodule top_module( input clk, input load, input [511:0] data, output [511:0] q ); wire [511:0] L,C,R; assign L = {1'b0,q[511:1]}; assign C = q; assign R = {q[510:0],1'b0}; always @(posedge clk) begin if(load) q <= data; else q <= (~C & R) | (~L & C) | (C & ~R); end endmodule Conway's Game of Life 16x16题意生命游戏是二维元胞自动机，每个细胞（cell）状态为1（活）或0（死），下一状态由8 个邻居的存活数决定：邻居存活数细胞下一状态0-1 个死亡（变 0）2 个保持当前状态3 个复活（变 1，即使之前是死的）≥4 个死亡（变 0）索引转换由一维索引转换到二维索引：行号 i：用idx 除以 16取整（舍去余数）；等效于： idx >> 4（右移 4 位 = 除以 2⁴）。列号 j：j = idx % 16（取模运算，即余数）；等效于按位与：idx & 15（15 的二进制是 1111，按位与之后达到保留低 4 位（即余数）的效果）。由二维索引转换到一维索引： (i, j) 的一维索引为 i * 16 + j.右移位运算符>> 是 右移位运算符：逻辑右移（>>）将二进制数向右移动，左侧空出的位用 0 填充，不考虑符号位。 语法：A >> N（将 A 右移 N 位）。算术右移（>>>）将二进制数向右移动，左侧空出的位用符号位填充（即保持负数的符号）。 语法：A >>> N（将 A 右移 N 位）。Solutionmodule top_module( input clk, input load, input [255:0] data, output [255:0] q ); reg [255:0] q_reg; // 模块级寄存器存储当前状态 assign q = q_reg; // 输出直接映射到寄存器 always @(posedge clk) begin // ------- 步骤1：声明过程块内的变量（必须放在块的开头）--------- reg [255:0] next_q; //存储下一状态（局部reg） integer idx; //当前细胞的一维索引 integer dr; //相对于当前细胞的行偏移（Delta Row） integer dc; //相对于当前细胞的列偏移（Delta Column） integer i; //当前细胞的二维行索引 integer j; //当前细胞的二维列索引 integer r; //邻居的绝对坐标的行索引 integer c; //邻居的绝对坐标的列索引 integer neighbor_idx; //邻居的一维索引 integer cnt; //邻居的存活数目 if (load) begin // 加载模式：直接赋值 q_reg <= data; end else begin // --------- 步骤2：遍历每个细胞，计算下一状态 ------------ for (idx = 0; idx < 256; idx = idx + 1) begin i = idx >> 4; // 计算当前细胞的行号（0~15） j = idx & 15; // 计算当前细胞的列号（0~15） cnt = 0; // 邻居存活数初始化 // 遍历8个邻居（dr, dc ∈ {-1, 0, 1}，左右位移只能取1或-1，所以待会需要跳过自身） for (dr = -1; dr <= 1; dr = dr + 1) begin for (dc = -1; dc <= 1; dc = dc + 1) begin if (dr == 0 && dc == 0) continue; // 跳过当前细胞 // 处理环面边界（确保行/列在0~15范围内） r = (i + dr + 16) % 16; // +16避免负数，再取模 c = (j + dc + 16) % 16; neighbor_idx = r * 16 + c; // 邻居的一维索引 // 统计存活邻居 if (q_reg[neighbor_idx]) begin cnt = cnt + 1; end end end // --------- 步骤3：根据规则更新下一状态 ------------- if (cnt < 2 || cnt >= 4) begin next_q[idx] = 1'b0; end else if (cnt == 2) begin next_q[idx] = q_reg[idx]; // 保持当前状态 end else begin // cnt == 3 next_q[idx] = 1'b1; // 复活 end end // 统一更新状态（时序安全，避免竞争） q_reg <= next_q; end end endmodule本题是硬件实现元胞自动机的经典方法！
4-bit shift register异步复位的优先级：areset为高时，立即复位（不管时钟和其他信号）。加载 vs 移位的优先级：若load为 1，无论和ena为何值，加载优先（先判断load）。右移操作的行为：右移时，q[3]补 0，q[0]被移出（例如：q=1010右移后变为0101）。Solutionmodule top_module( input clk, input areset, // async active-high reset to zero input load, input ena, input [3:0] data, output reg [3:0] q); always @(posedge clk or posedge areset) begin if (areset) begin q <= 4'b0000; end else begin if (load) begin q <= data; end else if(ena) begin q <= {1'b0,q[3:1]}; end end end endmodule Left/right rotatorSolutionmodule top_module( input clk, input load, input [1:0] ena, input [99:0] data, output reg [99:0] q); always @(posedge clk) begin if (load) // load q <= data; else if (ena) begin case(ena) 2'b00 : q <= q; // 其实这一行永远不会被执行！所以else分支可以被简化，见下方。 2'b01 : q <= {q[0],q[99:1]}; // 右旋转 （采用分块组合要比使用循环更简便） 2'b10 : q <= {q[98:0],q[99]};// 左旋转 2'b11 : q <= q; default q <= q; endcase end else begin // 当ena为0时执行 保持不变 q <= q; end end endmodule 简化else之后：always @(posedge clk) begin if (load) q <= data; else if (ena == 2'b01) q <= {q[0],q[99:1]}; // 右旋转 else if (ena == 2'b10) q <= {q[98:0],q[99]}; // 左旋转 // 其他情况（ena=2'b00或2'b11）保持不变 endLeft/right arithmetic shift by 1 or 8A 5-bit number 11000 arithmetic right-shifted by 1 is 11100, while a logical right shift would produce 01100. However, there is no difference between logical and arithmetic left shifts. Solutionmodule top_module( input clk, input load, input ena, input [1:0] amount, input [63:0] data, output reg [63:0] q); always @(posedge clk) begin if (load) begin q <= data; end else if (ena) begin case (amount) 2'b00: q <= {q[62:0],1'b0}; 2'b01: q <= {q[55:0],8'b0}; 2'b10: q <= {q[63],q[63:1]}; 2'b11: q <= {{8{q[63]}},q[63:8]}; //注意：位拼接语法 default: q <= q; endcase end end endmodule 5-bit LFSR线性反馈移位寄存器（LFSR） linear feedback shift register LFSR 是一种通过移位寄存器和反馈逻辑生成伪随机序列的电路。 Galois 型 LFSR 的特点：反馈逻辑通过 异或（XOR） 连接特定 “抽头”（tap）位，生成下一个状态的最低位（或最高位，依结构而定）。最大长度 LFSR（maximal-length）的抽头位置经过精心选择，可遍历 $2^n-1$ 个状态（n为位数，全零状态永远不会出现）。在本题中：位数：5 位（输出q[4:0]，q[4]是最高位，q[0]是最低位）抽头位置：题目指定抽头在位5和位3（编对应q[4]和q[2]）特殊说明：“抽头 5 的异或输入为 0” → 实际反馈逻辑为 q[4] ^ q[2]（异或 0 不改变值，等价于仅q[2]参与，但题目明确抽头是 5 和 3，故需保留异或）复位：同步高电平复位（reset为高时，q被置为 5'h1，即二进制 00001）移位方向：右移。其实，若只需简单解答此题，并不需要知道什么是tap。图中是一个由D触发器构成的阵列，按照图中连线即可画出。 Solutionmodule top_module( input clk, input reset, // Active-high synchronous reset to 5'h1 output [4:0] q ); always @(posedge clk) begin if (reset) // Load q <= 4'd1; else begin q[4] <= 1'b0 ^ q[0]; q[3] <= q[4]; q[2] <= q[3] ^ q[0]; q[1] <= q[2]; q[0] <= q[1]; end end endmodule下面是基于原理的官方Solution： module top_module( input clk, input reset, output reg [4:0] q); reg [4:0] q_next; // q_next is not a register always @(*) begin q_next = q[4:1]; // Shift all the bits. This is incorrect for q_next[4] and q_next[2] //q_next = q[4:1] 将当前状态q的高四位（q[4]~q[1]）赋值给 q_next 的低四位（q_next[3]~q_next[0]），实现了大部分位的右移。 q_next[4] = q[0]; // Give q_next[4] and q_next[2] their correct assignments //修正最高位 q_next[2] = q[0] ^ q[3]; //修正抽头位：该操作确保 LFSR 遍历 2^5 - 1=31 个状态（最大长度特性）。 end always @(posedge clk) begin if (reset) q <= 5'h1; else q <= q_next; end endmodule 3-bit LFSR二选一选择器： 本题仍是看图连线。 Solutionmodule top_module ( input [2:0] SW, // R input [1:0] KEY, // L and clk output [2:0] LEDR); // Q always @(posedge KEY[0]) begin LEDR[0] <= KEY[1] ? SW[0] : LEDR[2]; LEDR[1] <= KEY[1] ? SW[1] : LEDR[0]; LEDR[2] <= KEY[1] ? SW[2] : (LEDR[2] ^ LEDR[1]); end endmodule32-bit LFSR本题是 5-bit LFSR 的扩展，若实例化32个DDF显然不是很方便。 Solutionmodule top_module( input clk, input reset, // Active-high synchronous reset to 32'h1 output [31:0] q ); reg [31:0] q_next; always @(*) begin // 组合逻辑块（敏感列表为*） q_next = q[31:1]; q_next[31] = q[0]; q_next[21] = q[0] ^ q[22]; q_next[1] = q[0] ^ q[2]; q_next[0] = q[0] ^ q[1]; end always @(posedge clk) begin if (reset) q <= 32'h1; else q <= q_next; end endmodule Shift register (1)Solutionmodule top_module ( input clk, input resetn, // synchronous reset input in, output out); reg [3:0] Q; //记录每个DDF输出的Q always @(posedge clk ) begin if (!resetn) begin Q <= 0; end else begin Q[0] <= in; Q[1] <= Q[0]; Q[2] <= Q[1]; Q[3] <= Q[2]; end end assign out = Q[3]; endmodule Official Solutionmodule top_module ( input clk, input resetn, input in, output out ); reg [3:0] sr; // Create a shift register named sr. It shifts in "in". always @(posedge clk) begin if (~resetn) // Synchronous active-low reset sr <= 0; else sr <= {sr[2:0], in}; end assign out = sr[3]; // Output the final bit (sr[3]) endmodule Shift register (2)Solutionmodule top_module ( input [3:0] SW, input [3:0] KEY, output [3:0] LEDR ); MUXDFF u1 ( .clk(KEY[0]), .w(KEY[3]), .R(SW[3]), .E(KEY[1]), .L(KEY[2]), .Q(LEDR[3]) ); MUXDFF u2 ( .clk(KEY[0]), .w(LEDR[3]), //易疏忽：不再是KEY[3]了 .R(SW[2]), .E(KEY[1]), .L(KEY[2]), .Q(LEDR[2]) ); MUXDFF u3 ( .clk(KEY[0]), .w(LEDR[2]), .R(SW[1]), .E(KEY[1]), .L(KEY[2]), .Q(LEDR[1]) ); MUXDFF u4 ( .clk(KEY[0]), .w(LEDR[1]), .R(SW[0]), .E(KEY[1]), .L(KEY[2]), .Q(LEDR[0]) ); endmodule //引用自 Latches and Flip-Flops 的 Mux and DFF module MUXDFF ( input clk, input w, R, E, L, output Q ); wire mid1 = E ? w : Q; wire mid2 = L ? R : mid1; always @(posedge clk) begin Q <= mid2; end endmodule3-input LUT “移位寄存器设计” + “3 输入查找表（LUT）实现”8 位移位寄存器：输入 S 逐位移入，MSB（ Most Significant Bit） 先移入，enable 控制是否移位。8 个 D 触发器的输出标记为 Q[0] 到 Q[7]（移位顺序决定了 Q[0] 是最新移入的位，Q[7] 是最早移入的位）。S 连接到第一个触发器（Q[0]）的输入，每个触发器的输出连接到下一个触发器的输入。当 enable=1 时，每个时钟沿 clk 到来时：Q[0] 接收新输入 S；Q[1] 接收原来的 Q[0]；Q[2] 接收原来的 Q[1]；……Q[7] 接收原来的 Q[6]。 （效果：数据右移，Q[0] 是最新位，Q[7] 是最旧位，MSB 先存入 Q[7]。）3 输入查找表（LUT）：输入 A、B、C 作为地址（3 位，共 8 种组合：000~111）。输出 Z 根据地址选择移位寄存器的对应位：ABC=000 选 Q[0]，ABC=001 选 Q[1]，…，ABC=111 选 Q[7]。Solutionmodule top_module ( input clk, input enable, // 移位使能（高有效） input S, // 移位输入（MSB先移入） input A, B, C, // 3位地址输入 output Z ); reg [7:0] q; // 移位寄存器逻辑： always @(posedge clk) begin if (enable) begin q <= {q[6:0], S}; // 右移操作（左低位右高位，低位变到高位。但是目前索引中这样写是左高右低）：S→q[0]，q[0]→q[1]，…，q[6]→q[7] end end // 用A、B、C作为地址，直接用{A,B,C}制作索引 assign Z = q[{A, B, C}]; endmodule
Four-bit binary counterSolutionmodule top_module ( input clk, input reset, // Synchronous active-high reset output [3:0] q); always @(posedge clk) begin if(reset == 1) q <= 4'b0000; else if(q == 4'b1111) q <= 4'b0000; else q <= q + 1; end endmodule Decade counterSolutionmodule top_module ( input clk, input reset, // Synchronous active-high reset output [3:0] q); always @(posedge clk) begin if(reset == 1) q <= 4'b0000; else if(q == 4'b1001) q <= 4'b0000; else q <= q + 1; end endmodule Decade counter againSolutionmodule top_module ( input clk, input reset, output [3:0] q); always @(posedge clk) begin if(reset == 1) q <= 4'b0001; else if(q == 4'b1010) q <= 4'b0001; else q <= q + 1; end endmodule Slow decade counter暂停功能：仅当 slowena 为高电平时，计数器才会在时钟上升沿递增；否则保持当前值。 Solutionmodule top_module ( input clk, input slowena, input reset, output [3:0] q); always @(posedge clk) begin if(reset == 1) q <= 4'd0; else if(slowena) begin if(q == 4'd9) q <= 4'd0; else q = q + 1; end else q <= q; end endmodule Counter 1-12同步复位：reset 高电平时，时钟上升沿将计数器同步复位到 1。使能控制：enable 高电平时，计数器运行（计数）；enable 低电平时，计数器保持当前值（暂停）。**load 的核心作用:- **立即覆盖当前值**：当 `load=1` 时，无论计数器当前状态如何，都会在下一个时钟上升沿将 `d` 端口的值加载到计数器输出 `Q`。 - **优先级高于 `enable`**：即使 `enable=0`（计数器暂停），只要 `load=1`，计数器仍会加载 `d` 的值。 计数器的四种状态：loadenable计数器行为1X加载 d 的值（例如复位到 1 或回绕到 1）01正常计数（Q = Q + 1）00保持当前值（暂停）10仍加载 d 的值（load 优先级更高）已知：module count4( input clk, input enable, input load, input [3:0] d, output reg [3:0] Q ); Solutionmodule top_module ( input clk, input reset, input enable, output [3:0] Q, output c_enable, output c_load, output [3:0] c_d ); assign c_enable = enable; assign c_load = reset | (Q == 4'd12 && enable); assign c_d = c_load ? 4'b1 : 4'b0; count4 the_counter ( .clk(clk), .enable(c_enable), .load(c_load), .d(c_d), .Q(Q) ); endmoduleCounter 1000题中给出了一个十进制的模块，所以需要使用三次以达到把1000Hz降到1Hz. 已知：module bcdcount ( input clk, input reset, input enable, output reg [3:0] Q );Solutionmodule top_module ( input clk, input reset, output OneHertz, output [2:0] c_enable ); wire [3:0] one, ten, hundred; // 实例化三个 bcdcount 模块，分别表示个位、十位、百位 bcdcount counter0 (clk, reset, c_enable[0], one); // 个位 bcdcount counter1 (clk, reset, c_enable[1], ten); // 十位 bcdcount counter2 (clk, reset, c_enable[2], hundred); // 百位 // 使能信号控制逻辑 assign c_enable[0] = 1'b1; // 个位始终使能（持续计数） assign c_enable[1] = (one == 4'd9); // 十位仅在个位达到9时使能 assign c_enable[2] = (ten == 4'd9) && (one == 4'd9); // 百位仅在个位和十位都为9时使能 // 生成1Hz脉冲：当且仅当计数器达到999时，OneHertz输出高电平 assign OneHertz = (hundred == 4'd9) && (ten == 4'd9) && (one == 4'd9); endmodule4-digit decimal counter题目要求：构建一个 4 位 BCD（二进制编码十进制）计数器位分配：每个十进制位用 4 位二进制编码：q[3:0] → 个位（0~9），q[7:4] → 十位（0~9），q[11:8] → 百位（0~9），q[15:12] → 千位（0~9）（共 4 位，构成 0000~9999 的循环）。使能信号：对于 十位、百位、千位（即高位的三位），需输出对应的使能信号（如 ena[1] 对应十位、ena[2] 对应百位、ena[3] 对应千位），指示该高位何时应递增（例如：个位计满 9 时，触发十位的使能信号）。实现方式：可通过 实例化或修改 1 位十进制计数器（如 “十进制计数器模块”，实现 0~9 循环）来级联构建。信号解读：clk：时钟信号（上升沿触发计数）。reset：同步复位信号（高电平时，计数器复位到 0）。q[3:0]：个位的 BCD 值（如波形中依次为 7→8→9→0→1…，实现 0~9 循环）。ena[1]：十位的使能信号（仅当个位计满 9 时，ena[1] 置高，触发十位递增）。q[7:4]：十位的 BCD 值（如波形中 5→6→0…，受 ena[1] 控制，计满 9 后回 0）。Solutionmodule top_module ( input clk, input reset, // Synchronous active-high reset output [3:1] ena, output [15:0] q ); //实例化四个1位十进制计数器，分别表示个位、十位、百位、千位 count1bit u1 (clk,reset,enas,q[3:0]); count1bit u2 (clk,reset,ena[1],q[7:4]); count1bit u3 (clk,reset,ena[2],q[11:8]); count1bit u4 (clk,reset,ena[3],q[15:12]); //其中： wire enas; assign enas = 1'b1; //个位始终使能 // 生成十位的使能信号：当个位达到9时，ena[1]置高，触发十位计数 assign ena[1] = (q[3:0] == 4'b1001) ? 1'b1 : 1'b0; // 生成百位的使能信号：当十位和个位都达到9时，ena[2]置高，触发百位计数 assign ena[2] = ((q[7:4] == 4'b1001) && (q[3:0] == 4'b1001)) ? 1'b1 : 1'b0; assign ena[3] = ((q[11:8] == 4'b1001) && (q[7:4] == 4'b1001) && (q[3:0] == 4'b1001)) ? 1'b1 : 1'b0; endmodule //自主构建 1位十进制计数器 module count1bit ( input clk, input reset, input ena, // 使能信号：若高电平，则允许该计数器计数 output [3:0] q //4为BCD输出 ); always @(posedge clk) begin if(reset) q <= 4'b0000; else if(ena) begin if(q == 4'b1001) q <= 4'b0000; else q <= q + 1; end else q <= q; //使能无效时，保持当前值 end endmodule12-hour clockSolutionmodule top_module( input clk, input reset, input ena, output pm, output [7:0] hh, output [7:0] mm, output [7:0] ss); //------------------- 秒计数器（00-59） --------------------- always @(posedge clk) begin if (reset) ss <= 8'd0; else if (ena) begin if (ss[3:0] == 4'd9) begin //低位到9 ss[3:0] <= 4'd0; //清零低位 if (ss[7:4] == 4'd5) //低位到9并且高位到5 ss[7:4] <= 4'd0; //清零高位 else ss[7:4] <= ss[7:4] + 4'd1; end else ss[3:0] <= ss[3:0] + 4'd1; end end //------------------- 分计数器（00-59） --------------------- always @(posedge clk) begin if (reset) mm <= 8'd0; else if (ena && ss[7:4] == 4'd5 && ss[3:0] == 4'd9) begin //ena有效并且秒数为59时触发使能 if (mm[3:0] == 4'd9)begin //低位到9 mm[3:0] <= 4'd0; //清零低位 if (mm[7:4] == 4'd5) //低位到9并且高位到5 mm[7:4] <= 4'd0; //清零高位 else mm[7:4] <= mm[7:4] + 4'd1; end else mm[3:0] <= mm[3:0] + 4'd1; end end //------------------- 时计数器（01-12） --------------------- always @(posedge clk) begin if (reset) begin hh[7:4] <= 4'd1; //若直接写hh <= 8'd12; 是错误的，因为这是BCD码。 hh[3:0] <= 4'd2; pm <= 1'd0; end else if (ena && mm[7:4] == 4'd5 && mm[3:0] == 4'd9 && ss[7:4] == 4'd5 && ss[3:0] == 4'd9) begin //ena有效，并且秒数和分钟都为59时，触发使能 //低位到9并且高位为0 (09:59:59->10:00:00) if (hh[3:0] == 4'd9 && hh[7:4] == 4'd0) begin hh[3:0] <= 4'd0; hh[7:4] <= 4'd1; end //低位到2并且高位到1 (12:59:59->01:00:00) ，回绕回01 else if (hh[3:0] == 4'd2 && hh[7:4] == 4'd1) begin hh[7:4] <= 4'd0; hh[3:0] <= 4'd1; end //低位到1并且高位到1 (11:59:59->12:00:00) ，pm需要转换 else if (hh[3:0] == 4'd1 && hh[7:4] == 4'd1) begin hh[7:4] <= 4'd1; hh[3:0] <= 4'd2; pm <= ~pm; end else hh[3:0] <= hh[3:0] + 4'd1; end end endmodule一些小问题： 「在 Verilog 中，在always块中赋值的信号必须声明为reg型。你的代码中，pm、hh、mm、ss均在always块中被赋值，但声明时使用了默认的wire型（output pm、output [7:0] hh等），这会导致编译错误（“非法在 always 块中赋值 wire 型信号”）。」但是，在本问题中，直接赋值依然可以Success，这是因为本网站对语法检查不够严格.
D flip-flopClocked always blocks should use non-blocking assignments: <=.Solutionmodule top_module ( input clk, // Clocks are used in sequential circuits input d, output reg q );// // Use a clocked always block // copy d to q at every positive edge of clk always @(posedge clk) begin q <= d; end endmodule D flip-flopsSolutionmodule top_module ( input clk, input [7:0] d, output [7:0] q ); always @(posedge clk) begin q <= d; end endmoduleDFF with resetSolutionmodule top_module ( input clk, input reset, // Synchronous reset input [7:0] d, output [7:0] q ); always @(posedge clk) begin if (reset) begin //复位 q <= 8'b0; end else begin q <= d; end end endmodule DFF with reset value正触发: posedge 负触发: negedgeSolutionmodule top_module ( input clk, input reset, input [7:0] d, output [7:0] q ); always @(negedge clk) begin if (reset) begin q <= 8'h34; end else begin q <= d; end end endmodule DDF with asynchronous reset异步复位The only difference in code between synchronous and asynchronous reset flip-flops is in the sensitivity list——将复位信号写入敏感列表中。 Solutionmodule top_module ( input clk, input areset, // active high asynchronous reset input [7:0] d, output [7:0] q ); always @(posedge clk, posedge areset) begin if (areset) q <= 0; else q <= d; end endmodule DFF with byte enablewire vs. reg特性wirereg存储功能无存储功能，数值由驱动源决定有存储功能，能保持当前值直至下一次赋值赋值方式只能使用连续赋值（如assign语句）只能在initial或always块内赋值使用场景用于连线、组合逻辑输出用于时序逻辑（如触发器）、组合逻辑中的中间变量默认初始值默认值为z（高阻态）默认值为x（未知状态）下面通过两段代码示例，直观展示wire和reg的使用差异：// wire的使用示例 module wire_example; reg a, b; // 输入信号定义为reg类型 wire out; // 输出信号定义为wire类型 // 连续赋值，out的值会随着a和b的变化而立即改变 assign out = a & b; initial begin a = 1'b0; b = 1'b0; #10 a = 1'b1; #10 b = 1'b1; end endmodule// reg的使用示例 module reg_example; reg clk, rst, in; reg out; // 输出信号定义为reg类型，因为在always块中被赋值 // 时序逻辑：在时钟上升沿更新out的值 always @(posedge clk or posedge rst) begin if (rst) begin out <= 1'b0; // 复位时将out置为0 end else begin out <= in; // 时钟上升沿时将in的值赋给out end end initial begin clk = 1'b0; forever #5 clk = ~clk; // 生成周期为10的时钟信号 end endmodule同步低有效复位（resetn）同步性：复位仅在 时钟上升沿 生效（异步复位会在复位信号变化时立即生效，此处为同步）。低有效：resetn = 0 时触发复位，resetn = 1 时正常工作。字节使能（byteena[1:0]）分组控制：16 位数据分为 低字节（d[7:0]，由 byteena[0] 控制）和 高字节（d[15:8]，由 byteena[1] 控制）。功能：仅当对应 byteena 位为 1 时，该字节才会被更新；为 0 时，该字节保持原值。适用场景“16 位寄存器 + 字节级写保护 + 同步复位” （如： CPU 数据寄存器、总线接口……）Solutionmodule top_module ( input clk, input resetn, input [1:0] byteena, input [15:0] d, output [15:0] q ); always @(posedge clk) begin // 上升沿触发 if (!resetn) begin // 同步低有效复位：resetn=0 触发复位 q <= 16'b0; end else begin // 正常工作模式 // 低字节更新：byteena[0]=1则更新d[7:0]，否则保持q[7:0] q[7:0] <= byteena[0] ? d[7:0] : q[7:0]; // 高字节更新：byteena[1]=1则更新d[15:8]，否则保持q[15:8] q[15:8] <= byteena[1] ? d[15:8] : q[15:8]; end end endmoduleD Latch锁存器的状态更新依赖于控制信号（如使能信号 EN）的 “电平”，而非 “边沿”（上升沿或下降沿），这是它与触发器最核心的区别。 而触发器仅在时钟信号的特定边沿（如上升沿）才采样输入并更新输出，其他时间无论输入如何变化，输出都保持不变。 电平敏感的敏感列表： 由于锁存器对控制信号的 “电平” 敏感，always块的敏感列表必须包含所有可能触发输出变化的电平信号。 例如，对于 D 锁存器（输入 D、使能 EN、输出 Q），敏感列表需包含EN和D（因为当 EN 为有效电平时，D 的变化会直接影响 Q；当 EN 的电平变化时，也可能导致 Q 锁存或更新），即： always @(EN or D) begin ... end 非阻塞赋值（Non-blocking Assignment）：尽管锁存器是电平敏感，但作为时序元件（有记忆功能），其输出的更新并非 “立即响应” 输入（存在微小的存储延迟）。因此，Verilog 中需用非阻塞赋值（<=） 描述输出更新，以模拟这种 “延迟更新” 的特性，避免竞争条件。 例如：Q <= D;（而非阻塞赋值=，后者会立即更新，不符合时序元件的行为）。Solutionmodule top_module ( input d, input ena, output q); always @(ena or d) begin //level-sensitive sensitivity lists if(ena) q <= d; end endmoduleDFF (1)异步DFFSolutionmodule top_module ( input clk, input d, input ar, // asynchronous reset output q); always @(posedge clk,posedge ar) begin if(ar) q <= 0; else q <= d; end endmoduleDFF (2)同步DFFSolutionmodule top_module ( input clk, input d, input r, // synchronous reset output q); always @(posedge clk) begin if(r) q <= 0; else q <= d; end endmoduleDFF + gateSolutionmodule top_module ( input clk, input in, output out); always @(posedge clk) begin out <= out ^ in; //out可以同时存在于赋值的两侧 end endmodule Mux and DFF (1)Solutionmodule top_module ( input clk, input L, input r_in, input q_in, output reg Q); //Mux 2-to-1 wire mid; assign mid = L ? r_in : q_in; //DFF 同步 always @(posedge clk) begin Q <= mid; end endmodule Mux and DFF (2)Solutionmodule top_module ( input clk, input w, R, E, L, output Q ); wire mid1 = E ? w : Q; wire mid2 = L ? R : mid1; always @(posedge clk) begin Q <= mid2; end endmodule DDFs and gatesSolutionmodule top_module ( input clk, input x, output z ); wire Q1; wire Q2; wire Q3; wire mid1; wire mid2; wire mid3; assign mid1 = x ^ Q1; assign mid2 = x & ~Q2; assign mid3 = x | ~Q3; always @(posedge clk) begin Q1 <= mid1; Q2 <= mid2; Q3 <= mid3; end assign z = ~(Q1 | Q2 | Q3); endmodule Create circuit from truth tableSolutionmodule top_module ( input clk, input j, input k, output Q); always @(posedge clk) begin if ((j != k) && (j ^ k == 1)) Q <= j; else if (j == 0) Q <= Q; else Q <= ~Q; end endmodule Detect an edge精彩的动态过程检测 8 位输入信号中每个 bit 从 0 到 1 的上升沿变化，并在变化发生后的紧接着的下一个时钟周期将对应输出位置 1。核心思路：延迟采样 + 边沿检测延迟采样：用寄存器 prev_in 保存前一个时钟周期的输入信号 in，这样可以对比当前周期和前一周期的数值。边沿检测：对每个 bit，若当前周期是 1 且前一周期是 0，则说明发生了0→1的上升沿，对应输出位 pedge 置 1。Solutionmodule top_module ( input clk, input [7:0] in, output [7:0] pedge ); reg [7:0] pre_in; always @(posedge clk) begin pre_in <= in; end always @(posedge clk) begin pedge <= in & ~pre_in; //直观理解：随着时钟的进行，下一个周期将自动（重新检测）回落状态。 end endmoduleOfficial Solutionmodule top_module( input clk, input [7:0] in, output reg [7:0] pedge); reg [7:0] d_last; always @(posedge clk) begin d_last <= in; // Remember the state of the previous cycle pedge <= in & ~d_last; // A positive edge occurred if input was 0 and is now 1. end endmoduleDetect both edgesSolutionmodule top_module ( input clk, input [7:0] in, output [7:0] anyedge ); reg [7:0] pre_in; always @(posedge clk) begin pre_in <= in; anyedge <= (in & ~pre_in) | (~in & pre_in); end endmodule Edge capture register边沿捕获寄存器题目： 对于 32 位向量中的每个位，当输入信号在一个时钟周期内从 1 变为下一个时钟周期内的 0 时捕获。“捕获”意味着输出将保持为 1，直到寄存器被复位（同步复位）。 每个输出位的行为类似于 SR 触发器：在发生 1 到 0 的转换后的周期内，应将输出位设置为（1）。当复位为高电平时，应在正时钟沿将输出位复位为（0）。如果上述两个事件同时发生，则复位优先。在下面的示例波形的最后 4 个周期中，“复位（reset）”事件比“设置（set）”事件早一个周期发生，因此这里没有冲突。 在下面的示例波形中，为了清晰起见，再次分别显示了复位、in[1] 和 out[1]。 Solutionmodule top_module ( input clk, input reset, input [31:0] in, output [31:0] out ); reg [31:0] pre_in; always @(posedge clk) begin pre_in <= in; end always @(posedge clk) begin if(reset) out <= 32'b0; //同步复位 else out <= (~in & pre_in) | out; /** 1. out是上一周期的输出值; 2. 若检测到下降沿（~in & in_d 对应位为 1），则无论 out 原值如何，结果为 1（即：置位）； 3. 若未检测到下降沿，则结果等于 out 原值（即保持不变）。 **/ end endmoduleDual-edge triggered flip-flop巧妙的选择双向触发触发器Solutionmodule top_module ( input clk, input d, output q ); reg pre_d_pos, pre_d_neg; always @(posedge clk) begin pre_d_pos <= d; end always @(negedge clk) begin pre_d_neg <= d; end assign q = clk ? pre_d_pos : pre_d_neg; /** clk == 1 时，选择 “d的上一个上升沿的值”； clk == 0 时，选择 “d的上一个下升沿的值”； 即，output 具有“跟随的效果”：clk每跳变一次（无论上跳还是下调），q都将对“d的上一状态（pre_d_pos 或 pre_d_neg）”进行一次跟随。 **/ endmodule
Verilog 学习笔记（10）：Karnaugh Map to Circuit
K-Map: 卡诺图3-variable化简之后可得： Solutionmodule top_module( input a, input b, input c, output out ); // SOP form: Three prime implicants (1 term each), summed. // POS form: One prime implicant (of 3 terms) // In this particular case, the result is the same for both SOP and POS. assign out = (a | b | c); endmodule4-variable (1)Solutionmodule top_module( input a, input b, input c, input d, output out ); assign out = (~a & ~b & ~c) | (~a & b & ~c & ~d) | (~a & c & ~d) | (~a & b & c) | (a & c & d) | (a & ~b & ~c); endmodule 4-variable (2)Solutionmodule top_module( input a, input b, input c, input d, output out ); assign out = (a & ~c) | (~a & ~b & c) | (a & c); endmodule 4-variable (3)Solutionmodule top_module( input a, input b, input c, input d, output out ); assign out = a ^ b ^ c ^ d; endmoduleMinimum SOP and POSAB\CD0001111000(0)0(1)0(3)d(2)101(4)0(5)0(7)1(6)011(12)d(13)0(15)1(14)010(8)d(9)0(11)d(10)0（d is don't-care, which means you may choose to output whatever value is convenient.）对于最大项表达式，若单元格为0010（十进制 2），对应的最大项为 A+B+¬C+D（变量值为 0 时取原变量，为 1 时取反变量）。 Solutionmodule top_module ( input a, input b, input c, input d, output out_sop, output out_pos ); assign out_sop = (c & d) | (~a & ~b & c); assign out_pos = (c) & (~a | b | ~c) & (~b | ~c | d); endmoduleKarnaugh map (1)Solutionmodule top_module ( input [4:1] x, output f ); assign f = (~x[1] & x[3]) | (x[1] & x[2] & ~x[3]); endmodule Karnaugh map (2)Solutionmodule top_module ( input [4:1] x, output f ); assign f = (~x[1] & x[3]) | (~x[2] & ~x[3] & ~x[4]) | (x[2] & x[3] & x[4]) | (x[1] & ~x[2] & ~x[4]); endmoduleK-map implemented with a multiplexerSolutionmodule top_module ( input c, input d, output [3:0] mux_in ); //根据ab的输出观察cd的每一列 assign mux_in[0] = c | d; assign mux_in[1] = 0; assign mux_in[3] = c & d; //注意别把第三列和第四列搞反了 assign mux_in[2] = ~d; endmodule
Half adderSolutionmodule top_module( input a, b, output cout, sum ); assign cout = a & b; assign sum = a ^ b; endmodule A half adder adds two bits (with no carry-in) and produces a sum and a carry-out.Full adderSolutionmodule top_module( input a, b, cin, output cout, sum ); assign sum = a ^ b ^ cin; assign cout = (a & b) | (a & cin) | (b & cin); endmodule 3-bit binary adder三位加法器需 3 个全加器级联，将低位的进位输出连接到高位的进位输入，形成进位链。 Solutionmodule top_module( input [2:0] a, b, input cin, output [2:0] cout, output [2:0] sum ); assign sum[0] = a[0] ^ b[0] ^ cin; assign cout[0] = (a[0] & b[0]) | (b[0] & cin) | (a[0] & cin); assign sum[1] = a[1] ^ b[1] ^ cout[0]; assign cout[1] = (a[1] & b[1]) | (b[1] & cout[0]) | (a[1] & cout[0]); assign sum[2] = a[2] ^ b[2] ^ cout[1]; assign cout[2] = (a[2] & b[2]) | (b[2] & cout[1]) | (a[2] & cout[1]); endmodule AdderSolutionmodule top_module ( input [3:0] x, input [3:0] y, output [4:0] sum); wire cout0,cout1,cout2; FullAdder FA1 (x[0],y[0],1'b0,cout0,sum[0]); FullAdder FA2 (x[1],y[1],cout0,cout1,sum[1]); FullAdder FA3 (x[2],y[2],cout1,cout2,sum[2]); FullAdder FA4 (x[3],y[3],cout2,sum[4],sum[3]); endmodule //直接用一下全加器 module FullAdder( input a, b, cin, output cout, sum ); assign sum = a ^ b ^ cin; assign cout = (a & b) | (a & cin) | (b & cin); endmoduleSigned addition overflowA signed overflow occurs when adding two positive numbers produces a negative result, or adding two negative numbers produces a positive result.In addition，正数和负数相加时，因和的范围天然被补码覆盖，故无需判断溢出，必然无溢出。 BTW：// 加法运算 wire [7:0] sum = a + b; // 执行数值相加，考虑进位 // 按位与操作 wire [7:0] and_result = a & b; // 逐位AND，不考虑进位Solutionmodule top_module ( input [7:0] a, input [7:0] b, output [7:0] s, output overflow ); // assign s = ... // assign overflow = ... assign s = a + b; //仅需要考虑最高位（符号位）即可 assign overflow = (a[7] && b[7] && ~s[7] ) || (~a[7] && ~b[7] && s[7]); endmodule 100-bit binary adderSolutionmodule top_module( input [99:0] a, b, input cin, output cout, output [99:0] sum ); //assign sum = a ^ b ^ cin; //assign cout = (a & b) | (a & cin) | (b & cin); wire [99:0] cinm; assign cinm[0] = (a[0] & b[0]) | ( (a[0] | b[0]) & cin ); assign sum[0] = a[0] ^ b[0] ^ cin; generate genvar i; for (i = 1; i < 100; i = i + 1) begin : adder_loop //生成加法器链 assign sum[i] = a[i] ^ b[i] ^ cinm[i - 1]; assign cinm[i] = (a[i] & b[i]) | (a[i] & cinm[i -1]) | (b[i] & cinm[i -1]); end endgenerate //注意：generate 中是需要写assign的。 assign cout = cinm[99]; endmodule : adder_loop 是 生成块（generate block）的实例名称，用于标识和命名循环生成的硬件结构。 它是必需的语法元素，主要有以下两个作用：唯一标识生成的硬件实例所有硬件结构（如模块实例、连线等）必须有唯一的名称。当使用 for 循环生成多个相同结构时，每个迭代需要有不同的名称来区分。每个迭代会被扩展为 adder_loop[i] 的形式（如 adder_loop[1]、adder_loop[2] 等），确保每个实例名称唯一。支持层次化引用。4-digit BCD adderSolutionmodule top_module ( input [15:0] a, b, input cin, output cout, output [15:0] sum ); wire cout1,cout2,cout3; wire cout4; bcd_fadd u1 (a[3:0],b[3:0],cin,cout1,sum[3:0]); bcd_fadd u2 (a[7:4],b[7:4],cout1,cout2,sum[7:4]); bcd_fadd u3 (a[11:8],b[11:8],cout2,cout3,sum[11:8]); bcd_fadd u4 (a[15:12],b[15:12],cout3,cout4,sum[15:12]); //把cout4修改为cout就可以直接输出 assign cout = cout4; //其实以下两行完全没必要写 assign sum = {sum[15:12],sum[11:8],sum[7:4],sum[3:0]}; //sum已经通过实例化直接赋值 endmodule
2-to-1 multiplexerSolutionmodule top_module( input a, b, sel, output out ); assign out = sel ? b : a; endmodule 2-to-1 bus multiplexerSolutionmodule top_module( input [99:0] a, b, input sel, output [99:0] out ); assign out = sel ? b : a; //每一位允许自动匹配 endmodule 9-to-1 multiplexerSolutionmodule 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; //在输出端口16位宽的的时候不能直接写1 default: out = 16'hFFFF; // 设置默认值为全1 （16进制） endcase end endmodule 256-to-1 multiplexerVector indices can be variable, as long as the synthesizer can figure out that the width of the bits being selected is constant. In particular, selecting one bit out of a vector using a variable index will work. Solutionmodule top_module( input [255:0] in, input [7:0] sel, output out ); assign out = in[sel]; endmodule 256-to-1 4-bit multiplexerSolutionmodule top_module( input [1023:0] in, input [7:0] sel, output [3:0] out ); assign out = {in[4*sel+3],in[4*sel+2],in[4*sel+1],in[4*sel]}; //注意顺序应该是从高位到低位 endmodule
本节是一些练习巩固的习题。WireSolutionmodule top_module ( input in, output out); assign out = in; endmoduleGNDGND（Ground）： “地线” 或 “接地”。Solutionmodule top_module ( output out); assign out = 1'b0; endmoduleNORSolutionmodule top_module ( input in1, input in2, output out); assign out = ~(in1 | in2); //注意别忘了“非” endmoduleAnother gateSolutionmodule top_module ( input in1, input in2, output out); assign out = in1 & ~in2; endmoduleTwo gatesSolutionmodule top_module ( input in1, input in2, input in3, output out); wire m1; assign m1 = ~(in1 ^ in2); assign out = m1 ^ in3; endmoduleMore logic gatesSolutionmodule 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; assign out_or = a | b; assign out_xor = a ^ b; //异或 assign out_nand = ~(a & b); assign out_nor = ~(a | b); assign out_xnor = ~(a ^ b); //同或 assign out_anotb = a & ~b; //a 和 非b endmodule7420 chipSolutionmodule 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); endmoduleTruth tablesOne simple method to create a circuit that implements the truth table's function is to express the function in sum-of-products form. Sum (meaning OR) of products (meaning AND) means using one N-input AND gate per row of the truth table (to detect when the input matches each row), followed by an OR gate that chooses only those rows that result in a '1' output. 总结：推导 sum-of-products (SOP，积之和)的核心逻辑：用最小项 “标记” 所有输出为 1 的输入组合，再用 OR 运算 “汇总” 这些有效组合。由于最小项仅在唯一输入组合下为 1，OR 之后的表达式能精准复现真值表的输出逻辑。“找 1 行，写反 / 原变量乘积，再 OR”。 Solutionmodule 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); endmoduleTwo-bit equality通过直接比较两个 2 位输入 A 和 B 来判断它们是否相等，采用了 Verilog 的条件赋值语句即可。 Solutionmodule top_module ( input [1:0] A, input [1:0] B, output z ); assign z = (A == B) ? 1'b1 : 1'b0; endmoduleSimple circuit ASolutionmodule top_module (input x, input y, output z); assign z = (x ^ y) & x; endmoduleSimple circuit B先列出真值表：input: xinput: youtput: z001100010111Solutionmodule top_module ( input x, input y, output z ); assign z = (~x & ~y) | (x & y); endmoduleCombine circuits A and BSolutionmodule top_module (input x, input y, output z); wire z1,z2,z3,z4; moduleA u1 (.x(x), .y(y), .z(z1)); //通过实例化可以用A模块获得 z1 moduleB u2 (.x(x), .y(y), .z(z2)); moduleA u3 (.x(x), .y(y), .z(z3)); moduleB u4 (.x(x), .y(y), .z(z4)); wire m1,m2; assign m1 = z1 | z2; assign m2 = z3 & z4; assign z = m1 ^ m2; endmodule //module之间是并行的，所以不需要提前声明。 module moduleA (input x, input y, output z); assign z = (x ^ y) & x; endmodule module moduleB ( input x, input y, output z ); assign z = (~x & ~y) | (x & y); endmoduleRing or vibrate？响铃还是震动？ When designing circuits, one often has to think of the problem "backwards", starting from the outputs then working backwards towards the inputs. This is often the opposite of how one would think about a （sequential, imperative） programming problem, where one would look at the inputs first then decide on an action （or output）. For sequential programs, one would often think "If （inputs are --- ） then （output should be --- ）". On the other hand, hardware designers often think "The （output should be ---） when （inputs are ---）". Solutionmodule top_module ( input ring, input vibrate_mode, output ringer, // Make sound output motor // Vibrate ); //assign ringer = (ring ? 1'b1 : 1'b0); //Error //assign motor = (vibrate_mode ? 1'b1 : 1'b0); //Error assign ringer = ring & ~vibrate_mode; //有来电，且没有设置为震动模式时响铃 assign motor = ring & vibrate_mode; //有来电，且有设置为震动模式时震动 endmoduleThermostatSolutionmodule top_module ( input too_cold, input too_hot, input mode, input fan_on, output heater, output aircon, output fan ); assign heater = too_cold & mode; assign aircon = too_hot & ~mode; //assign fan = too_cold | too_hot | fan_on; //Logic Error assign fan = heater | aircon | fan_on; endmodule3-bit population countSolutionmodule top_module( input [2:0] in, output [1:0] out ); integer i; always @(*) begin out = 2'b00; for (i = 0; i < 3; i++) begin if(in[i] == 1'b1) out = out + 1; end end endmoduleGates and vectorsSolutionmodule top_module( input [3:0] in, output [2:0] out_both, output [3:1] out_any, output [3:0] out_different ); integer i; always @(*) begin for(i = 0; i < 3; i++) begin out_both[i] = in[i] & in[i + 1]; end for(i = 1; i < 4; i++) begin out_any[i] = in[i] | in[i - 1]; end for(i = 0; i < 4; i++) begin if(i == 3) out_different[3] = in[3] ^ in[0]; else out_different[i] = in[i] ^ in[i + 1]; end end endmodule Even longer vectorsmodule top_module( input [99:0] in, output [98:0] out_both, output [99:1] out_any, output [99:0] out_different ); integer i; always @(*) begin for(i = 0; i < 99; i++) begin out_both[i] = in[i] & in[i + 1]; end for(i = 1; i < 100; i++) begin out_any[i] = in[i] | in[i - 1]; end for(i = 0; i < 100; i++) begin if(i == 99) out_different[99] = in[99] ^ in[0]; else out_different[i] = in[i] ^ in[i + 1]; end end endmodule
Conditional ternary operatorposedgeposedge 是 Verilog 中用于检测信号上升沿的关键字，表示信号从低电平变为高电平的瞬间。非阻塞赋值非阻塞赋值的特性执行机制并发执行：非阻塞赋值在当前时间步的所有语句计算完成后，才统一更新变量值。延迟生效：赋值语句不会立即改变变量值，而是 “计划” 在当前时间步结束时更新。代码示例always @(posedge clk) begin a <= b; // 计划在时间步结束时将b的值赋给a b <= a; // 计划在时间步结束时将a的原始值赋给b end上述代码中，a和b会在时钟上升沿同时更新值，因此交换了彼此的数据。特性非阻塞赋值 (<=)阻塞赋值 (=)执行顺序并发执行，时间步结束时统一更新顺序执行，立即更新变量值适用场景时序逻辑（触发器、状态机）组合逻辑（算术运算、条件判断）潜在问题无竞争条件时序逻辑中可能引发竞争和毛刺典型应用always @(posedge clk)always @(*) 或 assignassignassign语句是用于描述组合逻辑的基本方式，它会持续监控右侧表达式的变化，并立即将结果赋值给左侧的信号。这种赋值方式对应实际硬件中的逻辑门电路，适合实现组合逻辑功能。Solutionmodule 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; endmoduleReduction operatorsThe _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]Solutionmodule top_module ( input [7:0] in, output parity); assign parity = ^ in; endmoduleReduction: Even wider gatesSolutionmodule 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; endmoduleCombinational for-loop: Vector reversal 2Solution (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 endmoduleSolution (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 endmoduleCombinational for-loop: 255-bit population countSolutionmodule 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 endmoduleGenerate for-loop: 100-bit binary adder 2Solutionmodule 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在 Verilog 中实例化多个相同结构需要使用 generate 语句。genvar 是一种特殊的变量类型，专门用于 生成块（generate 块） 中的循环索引。它的主要作用是在编译时展开循环，生成多个相同结构的硬件实例，而不是在运行时执行循环。编译时展开 genvar 声明的变量仅在编译时存在，用于控制 generate 块的循环次数。综合工具会将循环完全展开为多个独立的硬件实例，而非运行时的循环逻辑。与普通变量的区别普通变量（如 integer）：用于运行时的计算和控制流。genvar：仅用于编译时生成硬件结构，循环展开后变量本身不会存在于实际硬件中。作用域 genvar 只能在 generate 块内部使用，且必须在循环开始前声明。Generate for-loop: 100-digit BCD adderSolutionmodule 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
Always blocks (combinational)Solution// 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 Always blocks (clocked)Solution// 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 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.Procedural non-blocking assignment: ( x <= y; ). Can only be used inside a procedure.If statement语法格式：always @(*) begin if (condition) begin out = x; end else begin out = y; end end这等效于条件运算符的连续赋值：assign out = (condition) ? x : y;Solution// 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 ); always @(*) begin if (sel_b1 == 1 && sel_b2 == 1) begin // 注意：`=`是用于过程赋值的，而逻辑比较应该使用`==` out_always = b; end else begin out_always = a; end end assign out_assign = (sel_b1 == 1 && sel_b2 == 1) ? b : a; endmoduleIf statement latchesWhen designing circuits, you _must_ think first in terms of circuits:I want this logic gateI want a _combinational_ blob of logic that has these inputs and produces these outputsI want a combinational blob of logic followed by a set of flip-flopsWhat you _must not_ do is write the code first, then hope it generates a proper circuit.If (cpu_overheated) then shut_off_computer = 1;If (~arrived) then keep_driving = ~gas_tank_empty;在不提供 else clauses or a default value 的情况下，Verilog 将会 keep the outputs unchanged，这不是我们想要的。(因为本应 Combinational logic (e.g., logic gates) cannot remember any state.) 所以，Combinational circuits must have a value assigned to all outputs under all conditions. Solution// 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 // 添加else shut_off_computer = 0; end always @(*) begin if (arrived || gas_tank_empty) keep_driving = 0; else //添加else keep_driving = 1; end endmoduleTips:error 是 “流程跑不通”(工具拒绝执行，因为代码 / 设计非法)；incorrect 是 “流程跑得通，但结果不对”(代码合法，但逻辑错误)。Case statement语法格式：always @(*) begin // This is a combinational circuit case (in) 1'b1: begin out = 1'b1; // begin-end if >1 statement end 1'b0: out = 1'b0; default: out = 1'bx; endcase endThe case statement begins with case and each "case item" ends with a colon. There is no "switch".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. C does not allow duplicate case items.Solution// 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) 4'b0000: out = data0; 4'b0001: out = data1; 4'b0010: out = data2; 4'b0011: out = data3; 4'b0100: out = data4; 4'b0101: out = data5; default: out = 4'b0000; endcase // 勿忘 case 语句的结束关键字 end endmodulePriority encoderTips：'b：二进制(Binary)'o：八进制(Octal)'h：十六进制(Hexadecimal)'d：十进制(Decimal)module top_module ( input [3:0] in, output reg [1:0] pos ); always @(*) begin case(in) 4'b0000: pos = 0; 4'b0001: pos = 0; 4'b0010: pos = 1; 4'b0011: pos = 0; 4'b0100: pos = 2; 4'b0101: pos = 0; 4'b0110: pos = 1; 4'b0111: pos = 0; 4'b1000: pos = 3; 4'b1001: pos = 0; 4'b1010: pos = 1; 4'b1011: pos = 0; 4'b1100: pos = 2; 4'b1101: pos = 0; 4'b1110: pos = 1; 4'b1111: pos = 0; default: pos = 0; endcase end endmodule 记录从右(低位)向左数第一个高位的位置。Priority encoder with casezIt treats bits that have the value z as don't-care in the comparison. 语法格式：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 endSolution// 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 = 3'b000; 8'bzzzzzz1z: pos = 3'b001; 8'bzzzzz1zz: pos = 3'b010; 8'bzzzz1zzz: pos = 3'b011; 8'bzzz1zzzz: pos = 3'b100; 8'bzz1zzzzz: pos = 3'b101; 8'bz1zzzzzz: pos = 3'b110; 8'b1zzzzzzz: pos = 3'b111; default: pos = 3'b000; endcase end endmodule Avoiding latchesOne easy way to assign a "default value":always @(*) begin up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0; case (scancode) ... // Set to 1 as necessary. endcase endSolution// 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; 16'he072: down = 1; 16'he074: right=1; 16'he075: up = 1; endcase end endmodule