FPGA 中的线性反馈移位寄存器(LFSR)—— VHDL 与 Verilog 代码 LFSR 工作原理 LFSR(Linear Feedback Shift Register)在 FPGA 内部由一系列触发器串联成移位寄存器,从链路中抽取若干抽头接入 XOR 或 XNOR 门,门输出作为反馈接回链路起始端。这种结构资源占用少、可运行极高时钟频率,广泛用于: 计数器 测试模式发生器 数据扰码 密码学 LFSR 产生的序列为伪随机:接近随机但可预测(已知当前状态与抽头位置即可推导下一状态)。关键性质: 使用 XOR 反馈时,全 0 状态不会出现(0⊕0=0 导致锁死)。 使用 XNOR
设计 module single_port_sync_ram # (parameter ADDR_WIDTH = 4, parameter DATA_WIDTH = 32, parameter DEPTH = 16 ) ( input clk, input [ADDR_WIDTH-1:0] addr, inout [DATA_WIDTH-1:0] data, input cs, input we, input oe ); reg [DATA_WIDTH-1:0] tmp_data; reg [DATA_WIDTH-1:0] mem [DEPTH]; always @ (posedge clk
加法器是执行两数相加的数字组件。它是处理器 ALU 的核心组件,用于地址递增、表索引、缓冲指针等需要加法的场合。 全加器在两个输入二进制数之外加上进位输入,产生和与进位输出。 真值表 ABCinCoutSum 00000 00101 01001 01110 10001 10110 11010 11111 设计 下面展示一个 4 位加法器示例,通过信号 a 和 b 接受两个 4 位二进制数。加法器是组合电路,可用连续赋值 assign 或包含所有输入的敏感列表的 always 块建模。下面代码采用前者。 module fulladd (
什么是多路选择器? 多路选择器(mux)是一种数字元件,根据选择信号将 N 个输入中的一个传送到输出。下例中 N=4。例如 4 位多路选择器有 4 个 4 位输入,每个输入可通过选择信号传送到输出。 sel 为 2 位输入,可取四个值。选择线上的每个值允许一个输入传送到输出引脚 out。 sel a b c d out 0 3 7 1 9 3 1 3 7 1 9 7 2 3 7 1 9 1 3 3 7 1 9 9 4x1 多路选择器可用多种方式实现,这里展示两种最常用方式: 使用 assign 语句 使用 case 语句 使用 assign 语句 module m
设计 module pr_en ( input [7:0] a, input [7:0] b, input [7:0] c, input [7:0] d, input [1:0] sel, output reg [7:0] out ); always @ (a or b or c or d or sel) begin if (sel == 2b00) out <= a; else if (sel == 2b01) out <= b; else if (sel == 2b10) out <= c; else out
在数字电子学中,移位寄存器 是触发器的级联,一个触发器的输出 q 连接到下一个触发器的数据输入 (d)。因为所有触发器在同一时钟工作,移位寄存器中存储的位阵列将移动一位。例如,若一个 5 位右移寄存器初始值为 10110 且输入固定为 0,则下一个模式为 01011,再下一个为 00101。 设计 此移位寄存器设计有五个输入和一个 n 位输出,通过 parameter MSB 参数化表示移位寄存器宽度。若 n=4 则为 4 位,n=8 则为 8 位。 主要特性: 可通过 en 引脚启用或禁用 可通过 dir 控制左移或右移 若 rstn 拉低,复位寄存器输出变为 0 输入数据
设计 module gray_ctr # (parameter N = 4) ( input clk, input rstn, output reg [N-1:0] out ); reg [N-1:0] q; always @ (posedge clk) begin if (!rstn) begin q <= 0; out <= 0; end else begin q <= q + 1; `ifdef FOR_LOOP for (int i = 0; i < N-1; i
设计 module modN_ctr # ( parameter N = 10, parameter WIDTH = 4 ) ( input clk, input rstn, output reg[WIDTH-1:0] out ); always @ (posedge clk) begin if (!rstn) begin out <= 0; end else begin if (out == N-1) out <= 0; else out <= out + 1; end end
设计 module johnson_ctr #(parameter WIDTH=4) ( input clk, input rstn, output reg [WIDTH-1:0] out ); always @ (posedge clk) begin if (!rstn) out <= 1; else begin out[WIDTH-1] <= ~out[0]; for (int i = 0; i < WIDTH-1; i=i+1) begin out[i] <= out[i+1];
设计 module ring_ctr #(parameter WIDTH=4) ( input clk, input rstn, output reg [WIDTH-1:0] out ); always @ (posedge clk) begin if (!rstn) out <= 1; else begin out[WIDTH-1] <= out[0]; for (int i = 0; i < WIDTH-1; i=i+1) begin out[i] <= out[i+1]; end
脉冲 计数器是一种异步计数器,除第一个触发器外,其余触发器的时钟由前一个触发器的输出驱动。 设计 module dff ( input d, input clk, input rstn, output reg q, output qn ); always @ (posedge clk or negedge rstn) if (!rstn) q <= 0; else q <= d; assign qn = ~q; endmodule module ripple ( input clk, input rstn,
4 位计数器从 4b0000 递增至 4h1111,然后回滚到 4b0000。只要提供运行时钟且复位保持高电平,它将持续计数。 回滚发生在最终加法的最高有效位被丢弃时。当计数器达到最大值 4b1111 并再收到一个计数请求时,计数器试图达到 5b10000,但因只支持 4 位,最高位被丢弃,结果变为 0。 0000 0001 0010 ... 1110 1111 回滚 0000 0001 ... 设计包含两个输入:一个用于时钟,另一个用于低电平有效复位。低电平有效复位指复位引脚为 0 时复位生效。有一个 4 位输出 out 提供计数器值。 电子计数器设计 1)触发 // 进入块后,检查复
触发器在时钟的上升沿或下降沿捕获输入数据。关键在于,时钟沿之后直到下一个时钟沿之前,输入数据的任何变化都不会反映在输出上。而 锁存器 则不在时钟沿捕获,而是只要使能引脚有效,输出就跟随输入。 设计 本例构建一个具有三个输入和一个输出的锁存器。输入 d 为数据(0 或 1),rstn 为低电平有效复位,en 为使能信号,用于将输入数据锁存到输出。复位低电平有效意味着当该输入为 0 时复位生效。输出 q 的值由 d、en 和 rstn 决定。 module d_latch ( input d, // 1位数据输入 input en, // 1位使能输入 input r
设计 module tff ( input clk, input rstn, input t, output reg q ); always @ (posedge clk) begin if (!rstn) q <= 0; else if (t) q <= ~q; else q <= q; end endmodule 测试平台 module tb; reg clk; reg rstn; reg t; tff u0 ( .clk(clk), .rstn(rstn), .t
D 触发器 是一种时序单元,在时钟的指定沿跟随输入引脚 d 的值。 设计 #1:异步低电平有效复位 module dff ( input d, input rstn, input clk, output reg q ); always @ (posedge clk or negedge rstn) if (!rstn) q <= 0; else q <= d; endmodule 硬件原理图 测试平台 module tb_dff; reg clk; reg d; reg rstn; reg [2:0] del
设计 module jk_ff ( input j, input k, input clk, output q ); reg q; always @ (posedge clk) case ({j,k}) 2b00 : q <= q; 2b01 : q <= 0; 2b10 : q <= 1; 2b11 : q <= ~q; endcase endmodule 硬件原理图 测试平台 module tb_jk; reg j; reg k; reg clk;
开始学习最好从一个非常简单的例子入手,没有比 Hello World ! 更适合的了。 // 单行注释以双斜杠 // 开头 // Verilog 代码总是写在模块内,每个模块代表一个具有特定功能的数字模块 module tb; // initial 块是另一种结构,通常用于为仿真初始化信号网和变量 initial // Verilog 支持将信号值显示到屏幕,以便设计者调试电路 // 这里我们仅显示 Hello World $display (Hello World !); endmodule 名为 tb 且无输入输出端口的 module 作为仿真的顶
Verilog 提供了系统任务和函数,用于打开文件、向文件输出值、从文件读取值并加载到变量中,以及关闭文件。 打开和关闭文件 module tb; // 声明一个变量存储文件句柄 integer fd; initial begin // 以“写”权限打开名为 my_file.txt 的新文件 // 并将文件句柄指针存储在变量 fd 中 fd = $fopen(my_file.txt, w); // 关闭由 fd 指向的文件句柄 $fclose(fd); end endmodule 文件打开模式 参数
默认 timescale 虽然 Verilog 模块期望在模块前定义 timescale,但仿真器可能插入默认 timescale。Verilog 精细化层次中任意作用域的实际 timescale 可用系统任务 $printtimescale 打印,接受作用域作为参数。 module tb; initial begin // 打印此模块的 timescale $printtimescale(tb); // $printtimescale($root); end endmodule 可见即使模块前未放置 timescale 指令,仿真器仍应用了 1ns/1ns 的默认值。 仿真日志 xce
Verilog `timescale 指令指定仿真时间单位和精度。 Verilog $timeformat 系统函数控制显示语句(如 $display、$strobe)中 %t 格式说明符的报告风格。 语法 $timeformat(<unit_number>, <precision>, <suffix_string>, <minimum field width>); unit_number 是源码中所有 `timescale 指令中最小的时间精度 precision 表示当前 timescale 下小数位数 suffix_string 可选
Verilog