完成《实验指导书》实验三:
设计要求:

  • 实现“数据产生模块”的功能
  • 掌握嵌入式逻辑分析仪(SignalTap)的设置和使用(实验三附录)
  • 学习采用“功能仿真+芯片验证”方式进行芯片工程设计

伪随机数据序列发生器

通过寄存器不停地移位循环来实现伪随机的数据输出,代码如下。
这里ShiftReg定义为[7:1]或者是[1:7],异或的位数都是2,3,4,7,不会改变,只是如果1-7作为位置去理解,7-1作为高位低位去理解,图中右边为二进制的高位

module PnSeq_Gen5407(
	input clk,
	input rst_n,
	output seqout
);

reg [7:1] Shiftreg;

always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		Shiftreg[7] <= 1'b0;
		Shiftreg[6] <= 1'b1;
		Shiftreg[5] <= 1'b1;
		Shiftreg[4] <= 1'b0;
		Shiftreg[3] <= 1'b0;
		Shiftreg[2] <= 1'b0;
		Shiftreg[1] <= 1'b0;
	end else begin
		Shiftreg[7] <= Shiftreg[6];
		Shiftreg[6] <= Shiftreg[5];
		Shiftreg[5] <= Shiftreg[4];
		Shiftreg[4] <= Shiftreg[3];
		Shiftreg[3] <= Shiftreg[2];
		Shiftreg[2] <= Shiftreg[1];
		Shiftreg[1] <= Shiftreg[7]^Shiftreg[4]^Shiftreg[3]^Shiftreg[2];
	end
end

assign seqout = Shiftreg[7];

endmodule

数据产生模块

这里先定义了几个关键信号,随机数输出使能datagen_en,随机数据输出有效dataout_valid,随机数据输出dataout为实际产生的输出。
观察时序关系,要求为:模块接收到datagen_en的上升沿信号,在下一个时钟来临时dataout_valid置高表示开始输出,同时输出并行的随机数据。
我们看到上一个子模块里的输出seqout是一个1位宽的数据,但是在产生模块里,要求连续输出6个8位宽的随机数,每个时钟周期输出1个随机数,这里需要用到两个较为关键的逻辑

  • 串并转换
    定义一个模块内寄存器[7:0]DatReg,通过寄存器移位实现了“并行”
//串并变换
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		DatReg <= 8'b0;
	end else begin  //串行伪随机序列一次写入寄存器
//		DatReg <= {DatReg[6:0],seqout};
		DatReg[7] <= DatReg[6];
		DatReg[6] <= DatReg[5];
		DatReg[5] <= DatReg[4];
		DatReg[4] <= DatReg[3];
		DatReg[3] <= DatReg[2];
		DatReg[2] <= DatReg[1];
		DatReg[1] <= DatReg[0];
		DatReg[0] <= seqout;
	end
end
assign dataout = DatReg;  //输出8位并行随机数据
  • 计数逻辑
  • //输出数据计数
    always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
    	cnt_dout <= 3'd0;  //计数器初值置0
    end else begin
    	if(!delay_datagen_en&&datagen_en) begin  //使能信号出现上升沿
    		cnt_dout <= cnt_dout +3'b1;  //开始计数 
    	end else if(cnt_dout > 3'd0 && cnt_dout <= 3'd6) begin
    		cnt_dout <= cnt_dout +3'b1;
    	end else begin
    		cnt_dout <= 3'd0;
    	end
    end
    end
    

由于检测的是输出使能的上升沿,还需要一个边沿检测的小逻辑,最后给出完整模块代码:

module PixDat_Gen5407(
	input clk,
	input rst_n,
	input datagen_en,   //随机数输出使能
	output reg dataout_valid,  //随机数输出有效
	output [7:0] dataout  //随机数据输出
);

reg [7:0] DatReg;   //数据输出寄存器
reg [2:0] cnt_dout;   //计数器
reg delay_datagen_en;   //延时一拍判断上升沿
wire seqout;

PnSeq_Gen5407 p1(
	.clk(clk),
	.rst_n(rst_n),
	.seqout(seqout)
);

//串并变换
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		DatReg <= 8'b0;
	end else begin  //串行伪随机序列一次写入寄存器
//		DatReg <= {DatReg[6:0],seqout};
		DatReg[7] <= DatReg[6];
		DatReg[6] <= DatReg[5];
		DatReg[5] <= DatReg[4];
		DatReg[4] <= DatReg[3];
		DatReg[3] <= DatReg[2];
		DatReg[2] <= DatReg[1];
		DatReg[1] <= DatReg[0];
		DatReg[0] <= seqout;
	end
end
assign dataout = DatReg;  //输出8位并行随机数据


//使能信号延时
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		delay_datagen_en <= 1'b0;
	end else begin
		delay_datagen_en <= datagen_en;
	end
end

//输出数据计数
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		cnt_dout <= 3'd0;  //计数器初值置0
	end else begin
		if(!delay_datagen_en&&datagen_en) begin  //使能信号出现上升沿
			cnt_dout <= cnt_dout +3'b1;  //开始计数 
		end else if(cnt_dout > 3'd0 && cnt_dout <= 3'd6) begin
			cnt_dout <= cnt_dout +3'b1;
		end else begin
			cnt_dout <= 3'd0;
		end
	end
end

//产生数据输出有效信号
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		dataout_valid <= 1'b0;
	end else begin
		if(cnt_dout > 3'd0 && cnt_dout <= 3'd6) begin
			dataout_valid <= 1'b1;
		end else dataout_valid <= 1'b0;
	end 
end

endmodule

测试代码

`timescale 1ns/100ps

module test_PixDatGen5407;

reg clk;
reg rst_n;
reg datagen_en;
wire dataout_valid;
wire [7:0] dataout;

//实例化
PixDat_Gen5407 uut(
	.clk(clk),
	.rst_n(rst_n),
	.datagen_en(datagen_en),
	.dataout_valid(dataout_valid),
	.dataout(dataout)
);

initial begin
	clk = 1'b0;
	rst_n = 1'b0;
	datagen_en = 1'b0;
	#50;
	rst_n = 1'b1;
	#50;
	datagen_en = 1'b1;
	#10;
	datagen_en = 1'b0;
end

always begin
	clk = ~clk;
	#10;
end

endmodule

最后看到的仿真效果如下:

可以看到逻辑上是符合要求的。

下周实验准备

由于本人并没有学过RAM存储还有锁相环一系列操作,所以只写出了用寄存器代替的接收子模块的代码,并进行了简单的仿真,重要的是理解状态机转换的逻辑,先给出完整代码。

module Dat_Rx5407(
	input clk,
	input rst_n,
	input dataout_valid,     // 随机数输入有效
	input [7:0] dataout,     // 随机数据输入
	
	output reg waitdata,     // 等待数据输入
	output reg datavalid,    // 数据输出有效
	output reg [7:0] data    // 数据输出
);

reg [1:0] next_state, current_state;

localparam S_IDLE  = 2'b00;  // 空闲
localparam S_RECV  = 2'b01;  // 接收
localparam S_OUT   = 2'b10;  // 输出
localparam S_READY = 2'b11;  // 等待

integer i;

// 存储器用寄存器数组模拟RAM
reg [7:0] mem [0:5];    

reg [2:0] cnt_recv;   // 已接收数据个数:0~6
reg [2:0] cnt_send;   // 已输出数据个数:0~6

// 状态寄存器
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		current_state <= S_IDLE;
	end else begin
		current_state <= next_state;
	end
end

// 下一状态逻辑
always @(*) begin
	case(current_state)
		S_IDLE: begin
			next_state = S_READY;
		end
		S_READY: begin
			if(dataout_valid)
				next_state = S_RECV;
			else
				next_state = S_READY;
		end
		S_RECV: begin
			if(cnt_recv == 3'd6)
				next_state = S_OUT;
			else
				next_state = S_RECV;
		end
		S_OUT: begin
			if(cnt_send == 3'd6)
				next_state = S_IDLE;
			else
				next_state = S_OUT;
		end
		default: next_state = S_IDLE;
	endcase
end

// 时序逻辑
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		cnt_recv  <= 3'b0;
		cnt_send  <= 3'b0;
		waitdata  <= 1'b0;
		datavalid <= 1'b0;
		data      <= 8'd0;
		for(i = 0; i < 6; i = i + 1)
			mem[i] <= 8'd0;
	end else begin
		case(current_state)
			S_IDLE: begin
				waitdata  <= 1'b0;
				datavalid <= 1'b0;
				data      <= 8'd0;
				cnt_recv  <= 3'b0;
				cnt_send  <= 3'b0;
			end

			S_READY: begin
				waitdata  <= 1'b1;
				datavalid <= 1'b0;
				data      <= 8'd0;
				if(dataout_valid) begin
					mem[0]   <= dataout;
					cnt_recv <= 3'd1;
				end
			end

			S_RECV: begin
				waitdata  <= 1'b0;
				datavalid <= 1'b0;
				data      <= 8'd0;
				if(dataout_valid && (cnt_recv < 3'd6)) begin
					mem[cnt_recv] <= dataout;
					cnt_recv <= cnt_recv + 3'd1;
				end
			end

			S_OUT: begin
				waitdata <= 1'b0;
				if(cnt_send < 3'd6) begin
					datavalid <= 1'b1;
					data <= mem[cnt_send];
					cnt_send <= cnt_send + 3'd1;
				end else begin
					datavalid <= 1'b0;
					data <= 8'd0;
				end
			end

			default: begin
				cnt_recv  <= 3'b0;
				cnt_send  <= 3'b0;
				waitdata  <= 1'b0;
				datavalid <= 1'b0;
				data      <= 8'd0;
			end
		endcase
	end
end

endmodule

测试代码

`timescale 1ns/100ps

module test_PixDatGen5407;

reg clk;
reg rst_n;
reg datagen_en;
wire dataout_valid;
wire [7:0] dataout;

//实例化
PixDat_Gen5407 uut(
	.clk(clk),
	.rst_n(rst_n),
	.datagen_en(datagen_en),
	.dataout_valid(dataout_valid),
	.dataout(dataout)
);

initial begin
	clk = 1'b0;
	rst_n = 1'b0;
	datagen_en = 1'b0;
	#50;
	rst_n = 1'b1;
	#50;
	datagen_en = 1'b1;
	#10;
	datagen_en = 1'b0;
end

always begin
	clk = ~clk;
	#10;
end

endmodule


可以看到仿真的效果和老师给出的时序关系也是比较符合的,但是有一个细节上的小问题,就是由于我的状态机设置了4个状态,并且在S_IDLE待机到S_READY接收的状态是无条件转换的,这就导致在结束这个第一轮仿真之后,waitdata会被持续拉高,这个其实我还没有想好怎么解决,不过影响应该不是很大。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐