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

举报
李锐博恩 发表于 2021/07/15 03:49:24 2021/07/15
【摘要】 串并转换 1. 复习verilog语法 【选做题】 - 文件操作fopen fdisplay fwrite fclose - 生成随机数 random - 初始化 readmemh readmemb - finish stop 这几个我真没用过,先给一个优秀的链接:FPGA篇(四)Verilog系统函数介绍($display,$fopen,$fscanf,$fw...

串并转换

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:

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

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

模式0的串行输出正确。

模式1:

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

模式2:

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

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

模式3:

模式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位数声明,导致后面的功能出现问题,后来通过仿真图及时发现并得以改正。

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/89707184

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。