实验目标
我们可以利用8位移位寄存器来实现一个简单的随机数发生器。 经典的LFSR(线性反馈移位寄存器, Linear-feedback shift register )可以使用n位移位寄存器生成长度为 2n−1 的二进制循环序列。 这类序列的片段在表观上是随机的,所以被广泛用于通信中的随机序列生成。例如,在CDMA通信中的长码的长度就是 242−1 的伪随机序列。
具体实现时,可以用一个8位右移移位寄存器,从左到右的比特以 x7x6x5x4x3x2x1x0 表示。每个时钟周期右移一位, x0 被移出,最左边移入的位按照上一周期的值计算 1 :
x8=x4⊕x3⊕x2⊕x0
例如,初始二进制值为00000001时,移位寄存器的状态将按 00000001→10000000→01000000→001000000→00010000→10001000… 变化。 该序列的周期为255。 当然,当初始值为全零时,系统将一直停留在全零状态,所以需要对全零状态进行特殊处理。
请实现一个8位的周期为255的伪随机序列,以按钮为时钟信号,并请将8位二进制数以十六进制显示在数码管上,在DE10-Standard开发板上观察生成的随机数序列。
实验收获
1. 理解如何通过移位寄存器来实现随机数发生器
2. 复位信号不起作用 ,还不清楚是什么原因
实验过程
1. 新增lfsr_8bit.v 文件
利用8位移位寄存器来实现一个经典的LFSR的随机数发生器。 为了方便查看,增加了一个延时机制。
module lfsr_8bit (
input clk, // 时钟输入(按钮)
input rst_n, // 复位信号(低电平有效)
output reg [7:0] q // 8位LFSR输出
);
wire feedback;
parameter CLK_NUM = 500000;
reg [31:0] count;
// 对于8位LFSR,使用抽头位置7、5、4、3(X^8 + X^7 + X^5 + X^4 + X^3 + 1)
assign feedback = q[7] ^ q[5] ^ q[4] ^ q[3];
always @(posedge clk ) begin
if (rst_n) begin
// 复位时设置非零初始值(例如00000001)
q <= 8'b00000001;
count <= 0; // 复位计数器
end else begin
// 特殊处理:如果当前状态为全零,强制设置为非零值
if (q == 8'b00000000) begin
q <= 8'b00000001;
end else begin
// 每CLK_NUM个时钟周期更新一次LFSR状态
if(count == CLK_NUM) begin q <= {q[6:0],feedback}; end // 右移一位,最左边移入反馈值
count <= (count == CLK_NUM) ? 0 : count + 1;
end
end
end
endmodule
2. 新增lfsr_8bit_seg.v文件
因为需要用十六位进制的显示,因此只需到了SEG0和SEG1两个SEG。其它7个SEG进行了熄灯操作。
module lfsr_8bit_seg(
input wire clk, // 时钟输入
input wire rst, // 复位信号(低电平有效)
input wire [7:0] data, // 8位数据输入
output [7:0] o_seg0,
output [7:0] o_seg1,
output [7:0] o_seg2,
output [7:0] o_seg3,
output [7:0] o_seg4,
output [7:0] o_seg5,
output [7:0] o_seg6,
output [7:0] o_seg7
);
// 七段数码管编码表(共阳极,段顺序:dp,g,f,e,d,c,b,a)
reg [7:0] seg_table [0:15];
reg [7:0] seg0, seg1;
// 初始化七段编码表
initial begin
seg_table[0] = 8'b11111100; // 0 → abcdef亮 [1](@ref)
seg_table[1] = 8'b01100000; // 1 → bc亮
seg_table[2] = 8'b11011010; // 2 → abdeg亮
seg_table[3] = 8'b11110010; // 3 → abcdg亮
seg_table[4] = 8'b01100110; // 4 → bcfg亮
seg_table[5] = 8'b10110110; // 5 → acdfg亮
seg_table[6] = 8'b10111110; // 6 → acdefg亮
seg_table[7] = 8'b11100000; // 7 → abc亮
seg_table[8] = 8'b11111110; // 8 → abcdefg全亮(缺dp)[1,
seg_table[9] = 8'b11110110; // 9 → abcdfg亮(缺e段)
seg_table[10] = 8'b11101110; // A(10) → abcefg亮(缺d段)
seg_table[11] = 8'b00111110; // B(11) → cdefg亮(缺ab段)
seg_table[12] = 8'b10011100; // C(12) → adef亮(缺bcg段)
seg_table[13] = 8'b01111010; // D(13) → bcdeg亮(缺af段)
seg_table[14] = 8'b11011110; // E(14) → adefg亮(缺bc段)
seg_table[15] = 8'b11101110; // F(15) → aefg亮(缺bcd段)
end
// 仅使用前两位数码管显示8位数据(高4位和低4位)
always @(posedge clk ) begin
if (rst) begin
seg0 <= 8'b11111111; // 复位时关闭所有段
seg1 <= 8'b11111111; // 复位时关闭所有段
end else begin
// 使用低4位和高4位数据来显示
// 注意:这里假设data是8位二进制数,seg_table的索引范围是0-15
// 因此data[3:0]和data[7:4]分别对应低4位和高4位
seg0 <= ~ seg_table[data[3:0]];
seg1 <= ~ seg_table[data[7:4]];
end
end
assign o_seg0 = seg0 ;
assign o_seg1 = seg1 ;
assign o_seg2 = 8'b11111111; // dp段关闭
assign o_seg3 = 8'b11111111; // dp段关闭
assign o_seg4 = 8'b11111111; // dp段关闭
assign o_seg5 = 8'b11111111; // dp段关闭
assign o_seg6 = 8'b11111111; // dp段关闭
assign o_seg7 = 8'b11111111; // dp段关闭
endmodule
3. 修改top.v 文件
例化lfsr模块和对应的SEG 显示模块
module top(
input clk,
input rst,
input [4:0] btn,
input [15:0] sw,
input ps2_clk,
input ps2_data,
input uart_rx,
output uart_tx,
output [15:0] ledr,
output VGA_CLK,
output VGA_HSYNC,
output VGA_VSYNC,
output VGA_BLANK_N,
output [7:0] VGA_R,
output [7:0] VGA_G,
output [7:0] VGA_B,
output [7:0] seg0,
output [7:0] seg1,
output [7:0] seg2,
output [7:0] seg3,
output [7:0] seg4,
output [7:0] seg5,
output [7:0] seg6,
output [7:0] seg7,
output [7:0] q, //实验六:8bit LFSR
output [3:0] result, //实验三:简易4bit ALU
output zero, //实验三:简易4bit ALU
output overflow,//实验三:简易4bit ALU
output carry //实验三:简易4bit ALU
);
//实验六:8bit LFSR
// output declaration of module lfsr_8bit
//reg [7:0] q;
lfsr_8bit u_lfsr_8bit(
.clk (clk ),
.rst_n (rst ),
.q (q )
);
lfsr_8bit_seg u_lfsr_8bit_seg(
.clk (clk ),
.rst (rst ),
.data (q ),
.o_seg0 (seg0 ),
.o_seg1 (seg1 ),
.o_seg2 (seg2 ),
.o_seg3 (seg3 ),
.o_seg4 (seg4 ),
.o_seg5 (seg5 ),
.o_seg6 (seg6 ),
.o_seg7 (seg7 )
);
4.修改 main.cpp文件
在main中添加 对随机数的跟踪校对
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <nvboard.h>
#include "Vtop.h"
#include "verilated.h"
#include "verilated_fst_c.h" //用来生成.fst文件
static TOP_NAME* dut;
static VerilatedContext* contextp; // 全局变量
static VerilatedFstC *tfp; // 全局变量
void nvboard_bind_all_pins(TOP_NAME* top);
static void single_cycle() {
// 低电平周期
dut->clk = 0;
dut->eval();
tfp->dump(contextp->time());
contextp->timeInc(5); // 假设时钟周期为10个时间单位
// 高电平周期
dut->clk = 1;
dut->eval();
tfp->dump(contextp->time());
contextp->timeInc(5);
}
static void reset(int n) {
dut->rst = 1;
while (n -- > 0) single_cycle();
dut->rst = 0;
}
int main(int argc, char** argv) {
contextp = new VerilatedContext; // 初始化全局变量
Vtop* top = new Vtop{contextp};
dut = top;
tfp = new VerilatedFstC; // 初始化全局变量
contextp->commandArgs(argc, argv);
contextp->traceEverOn(true); //打开追踪
top->trace(tfp, 99); // 增加追踪深度
tfp->open("wave.fst"); //保存位置
nvboard_bind_all_pins(dut);
nvboard_init();
reset(10);
int count = 0;
while (!contextp->gotFinish()) {
nvboard_update();
single_cycle();
count++;
// 实验三: printf("A = %d,B = %d ,opcode = %b, result = %3d ,zero = %d ,overflow = %d ,carry = %d \n",
// top->sw & 0x0f,(top->sw >> 4) & 0x0f,(top->sw >> 8) & 0x07, top->result, top->zero, top->overflow, top->carry);
printf("lfsr q = 0x%x \n", top->q );
if(count > 300000000)
break;
}
delete top;
tfp->close();
delete contextp;
return 0;
}
5. 运行结果
打印结果和SEG 的显示值是一致的。
6.遗留问题
刚觉复位信号有问题,还没搞明白是怎么回事。