【Verilog HDL 训练】第 07 天(串并转换)

串并转换

1. 复习verilog语法

【选做题】

- 文件操作fopen fdisplay fwrite fclose

- 生成随机数 random

- 初始化 readmemh readmemb - finish stop


这几个我真没用过,先给一个优秀的链接:FPGA篇(四)Verilog系统函数介绍($display,$fopen,$fscanf,$fwrite($fdisplay),$fclose,$random,$stop)

再借鉴一篇:

本题借鉴别人的:verilog-day7.md

  • 文件操作fopen fdisplay fwrite fclose

     integer fileunder;
     fileunder = $fopen("FileName");//打开文件,返回一个整型,会清空文件
     $fdisplay(fileunder,"%d",mumber);//写入操作,写完换行
     $fwrite(fileunder,"%d",mumber);//写入操作,写完不换行
     $fclose(fileunder);//关闭文件
  • 生成随机数random

    每次调用$random任务时,它返回一个32位带符号的随机整数。将$random放入{}内,可以得到非负整数。$random(seed)中的seed是一个整数,用于指出随机数的取值范围。

     rand = $random % 60;//给出了一个范围在-59到59之间的随机数。
     rand = {$random} % 60;//通过位并接操作产生一个值在0到59之间的数。
     rand = min+{$random} % (max-min+1);//产生一个在min, max之间随机数的例子。
  • 初始化 readmemh readmemb

    把文本文件的数据读到存储器阵列中,以对存储器阵列完成初始化。

     $readmemb("<数据文件名>",<存储器名>);
     $readmemb("<数据文件名>",<存储器名>,<起始地址>);
     $readmemb("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);
     
     $readmemh("<数据文件名>",<存储器名>);
     $readmemh("<数据文件名>",<存储器名>,<起始地址>);
     $readmemh("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);
     
     $readmemb中要求数据必须为二进制,$readmemh要求数据必须为十六进制
  • finish stop

    $stop:用于在仿真时,暂停仿真。运行到$stop的时候,仿真会暂停;此时可以在命令行输入run继续运行仿真。

    $finish:仿真停止。运行到$finish的时候,仿真停止退出,此时不可以再继续运行


2. 用verilog实现串并变换。

input [3:0] data_in; output [3:0] data_out; input [1:0] mode; input clk; input rst_n;

mode 0 :串行输入data_in[0],并行输出data_out[3:0]

mode 1 :并行输入data_in[3:0],串行输出data_out[0]

mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期

mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序

data_out[3]=data_in[0];

data_out[2]=data_in[1]

data_out[1]=data_in[2]

data_out[0]=data_in[3]

附加要求【选做】 将输入输出的位宽做成参数化


先写一个四选一的多路选择器,供顶层文件使用:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2019/05/03 13:46:33
// Design Name: 
// Module Name: mux2b
//


module mux2b #(parameter N = 4) (
	input [1 : 0]mode,
	input [N-1 : 0] a,
	input [N-1 : 0] b,
	input [N-1 : 0] c,
	input [N-1 : 0] d,
	output reg [N-1 : 0] data_out

    );
	
	localparam M0 = 2'b00, M1 = 2'b01, M2 = 2'b10, M3 = 2'b11;
	
	always @ (*) begin
		case(mode)
			M0: data_out = a;
			M1: data_out = b;
			M2: data_out = c;
			M3: data_out = d;
			default: ;
		endcase
	end
	
	
endmodule

顶层文件:

`timescale 1ns / 1ps
//
// Create Date: 2019/04/30 13:00:03
// Design Name: 
// Module Name: Serial2Parallel
// Revision 0.01 - File Created
// Additional Comments:
// 2. 用verilog实现串并变换。

/* input [3:0] data_in; output [3:0] data_out; input [1:0] mode; input clk; input rst_n;

mode 0 :串行输入data_in[0],并行输出data_out[3:0] 

mode 1 :并行输入data_in[3:0],串行输出data_out[0]

mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期 

mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序 

data_out[3]=data_in[0];

data_out[2]=data_in[1] 

data_out[1]=data_in[2] 

data_out[0]=data_in[3] 

附加要求【选做】 将输入输出的位宽做成参数化  */
//


module Serial2Parallel #(parameter N = 4) (
	input [N-1:0] data_in,
	input [1:0] mode,
	input clk,
	input rst_n,
	output [N-1:0] data_out

    );
	
	localparam 
	M0 = 2'b00,  //mode 0 :串行输入data_in[0],并行输出data_out[3:0] 
	M1 = 2'b01,  //mode 1 :并行输入data_in[3:0],串行输出data_out[0]
	M2 = 2'b10,  //mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期 
	M3 = 2'b11;  //mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序 
	
	reg [N-1:0] data_out_mid0, data_out_mid1,data_out_mid2,data_out_mid3;
	integer i;

//----------------------------------------------------------------
//en0上升沿检测
	reg en0;
	reg en0_r0,en0_r1;
	always @ (posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			en0 <= 0;
			en0_r0 <= 0;
			en0_r1 <= 0;
		end
		else begin
			en0_r0 <= en0;
			en0_r1 <= en0_r0;
		end
	end
	
	wire pos0_en,pos0_en1;
	assign pos0_en = ~en0_r0 & en0;
	assign pos0_en1 = ~en0_r1 & en0_r0;
	
	//en1上升沿检测
	reg en1;
	reg en1_r0,en1_r1;
	always @ (posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			en1 <= 0;
			en1_r0 <= 0;
			en1_r1 <= 0;
		end
		else begin
			en1_r0 <= en1;
			en1_r1 <= en1_r0;
		end
	end
	
	wire pos1_en,pos1_en1;
	assign pos1_en = ~en1_r0 & en1;
	assign pos1_en1 = ~en1_r1 & en1_r0;
	
	//en2上升沿检测
	reg en2;
	reg en2_r0,en2_r1;
	always @ (posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			en2 <= 0;
			en2_r0 <= 0;
			en2_r1 <= 0;
		end
		else begin
			en2_r0 <= en2;
			en2_r1 <= en2_r0;
		end
	end
	
	wire pos2_en,pos2_en1;
	assign pos2_en = ~en2_r0 & en2;
	assign pos2_en1 = ~en2_r1 & en2_r0;
	
//-----------------------------------------------------------------------	
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			data_out_mid0 <= 0;
			data_out_mid1 <= 0;
			data_out_mid2 <= 0;
			data_out_mid3 <= 0;
		end
		else begin
			case(mode)
			M0: begin
				data_out_mid0 <= {data_out_mid0[N-2:0],data_in[0]};//每次输入一位data_in[0]
			end
			M1: begin
				if(pos0_en) begin
					data_out_mid1 <= data_in;
				end
				else if(pos0_en1)begin
					data_out_mid1 <= data_out_mid1;
				end
				else begin
					data_out_mid1 <= {data_out_mid1[0],data_out_mid1[N-1:1]};
				end
				
			end
			M2: begin
				if(pos1_en) begin
					data_out_mid2 <= data_in;
				end
				else if(pos1_en1)begin
					data_out_mid2 <= data_out_mid2;
				end
			end
			M3: begin
				if(pos2_en) begin
					data_out_mid3 <= data_in;
				end
				else if(pos2_en1)begin
					//genvar i;
					//generate 
					for(i = 0; i < N; i = i + 1) begin 
						data_out_mid3[i] <= data_out_mid3[N-1-i]; 
					end 
					//endgenerate
				end
			
			end
			default: ;
			endcase
		
		end
	end
	
	always @ (*) begin
		case(mode)
		M0:;
		M1: en0 = 1;
		M2: en1 = 1;
		M3: en2 = 1;
		default: ;
		endcase
	end
	
	mux2b #(.N(4)) u_data_out(
	.mode(mode),
	.a(data_out_mid0), //mode0
	.b(data_out_mid1), //mode1
	.c(data_out_mid2), //mode2
	.d(data_out_mid3), //mode3
	.data_out(data_out)
	);
	
endmodule

给出测试文件:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2019/04/30 21:17:46
// Design Name: 
// Module Name: Serial2Parallel_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module Serial2Parallel_tb(

    );
	
	reg clk, rst_n;
	reg [3:0] data_in;
	reg [1:0] mode;
	wire data_out;
	
	localparam 
	M0 = 2'b00,  //mode 0 :串行输入data_in[0],并行输出data_out[3:0] 
	M1 = 2'b01,  //mode 1 :并行输入data_in[3:0],串行输出data_out[0]
	M2 = 2'b10,  //mode 2 :并行输入data_in[3:0],并行输出data_out[3:0],延迟1个时钟周期 
	M3 = 2'b11;  //mode 3 :并行输入data_in[3:0],并行反序输出data_out[3:0],延迟1个时钟周期并且交换bit顺序 
	
	initial clk = 0;
	always begin
		#1 clk = ~clk;
	end
	
	initial begin
		rst_n = 0;
		
		#3
		rst_n = 1;
		mode = M0;
		data_in = 4'b0001;
		#2 
		data_in = 4'b0000;
		#2 
		data_in = 4'b0001;
		#2
		data_in = 4'b0000;
		#2 
		data_in = 4'b0001;
		
		#5
		mode = M1;
		data_in = 4'b0100;
		
		#12
		mode = M2;
		data_in = 4'b0010;
		
		#6
		mode = M3;
		data_in = 4'b1010;
		

	
	
	end
	
	Serial2Parallel #(.N(4)) u0(
	.clk(clk),
	.rst_n(rst_n),
	.mode(mode),
	.data_in(data_in),
	.data_out(data_out)
	);
	
	
endmodule

下面给出行为仿真波形图:

模式0:

【Verilog HDL 训练】第 07 天(串并转换)_第1张图片

由于输入是data_in[0],所有data_in[0]单独显示也许会更好:

【Verilog HDL 训练】第 07 天(串并转换)_第2张图片

由仿真图可见,我们的串行输入为1010111,而输出为0001,0010,0101,1010,0101,1011,0111,符合我们的并行输出。

模式0的串行输出正确。

模式1:

【Verilog HDL 训练】第 07 天(串并转换)_第3张图片

模式一为并行输入,串行输出,输出为data_out[0],就是上图中的[0],模式1从16ns处开始,在17ns时刻(时钟上升沿)给数据,缓冲一个周期,到19ns开始输出,输出为0010;因为输入从低到高为0010,故输出4个即可,其他输出均不是我们要的。

模式2:

【Verilog HDL 训练】第 07 天(串并转换)_第4张图片

模式2是并行输入,并行输出:

模式2从28ns处开始,在29ns处(时钟有效沿)给数据31ns处输出数据data_out为0010;

模式3:

【Verilog HDL 训练】第 07 天(串并转换)_第5张图片

模式3为并行输入,并行输出,但是反向输出,例如输入为data_in = 4'b1010,则输出data_out=4'b0101;

模式3从34ns开始,35ns给数据从低到高为0101,延迟一拍,输出为1010,满足要求。

总结:代码需要多些,多学习一些技巧,这次写的这个代码,我自己是很不满意的,因为有很多缺陷,并不能完美的实现所要的功能(例如,输出延迟一拍,那一拍之前的输出是什么?是不是应该不输出,设为高阻态),但碍于见识少等,这种困惑并不能在代码中体现。


3. 记录一下第2题中用到的工具,包括工具版本,操作步骤或命令选项,遇到的错误,提示信息等。比较一下,与昨天的记录有何相同,有何不同。

Vivado 2018

这次的代码设计用了不少时间,因为加入了mode,模式之间的切换是一个需要考虑的难点。第二道题,确实也让我学到了一些东西,这都可以成为经验,我一开始的设计,最后的data_out输出并没有使用多路选择器来实现,而是如下:

assign data_out = data_out_mid0;

assign data_out = data_out_mid1;

我现在看起来确实可笑,但是当时并没有想到,我在相关群里咨询了大佬们,大佬们一眼就看出来了问题,确实感谢它们。

上述写法,data_out和这么多变量相连,到底输出取谁呢?想想都知道走火入魔了。

但最终的整体程序,还是个人最初的思想,加以修改。

这肯定不是最好的写法,还是要想别人学习。多见识见识电路实现的技巧。

最后一个简单的问题就是,我调试代码中犯的一个小错误,就是在多路选择器的代码中,忘了给mode位数声明,导致后面的功能出现问题,后来通过仿真图及时发现并得以改正。

 

 

你可能感兴趣的:(#)