【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:
由于输入是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
- 点赞
- 收藏
- 关注作者
评论(0)