/2021.08.05修改
今天和一个朋友讨论这个题,发现我之前写的代码存在两个问题:第一,不能直接对多bit数据进行打两拍;第二,这个写法,没有考虑到clk_out是clk_in的四倍及其以上,所以状态机回到1‘b0后仍然会跳转到1‘b1,会重复采数据(原来从1‘b0跳转到1‘b1的条件是valid_i_r1为1,这是不全面的)。所以问题的关键是怎么判断一次输入数据传输的开始和结束。大家可以先思考一下,第一部分和第二部分是不太对的,正确的结果放在第三部分。
//2020.08.02错误版本
修正后的部分见第三部分。
使用verilog或者VHDl设计一个从4bit到1bit的转换电路,valid_i/valid_o为高电平代表输入/输出数据是有效的,valid_i可能不连续,clk_in是d_i的时钟,clk_out是d_o的时钟,clk_out的频率是clk_in的频率的4倍及以上,rst为异步复位信号,高有效。
一下代码是经过仿真测试了功能的。
1、verilog代码
设计思路是:先用同步器将有效信号valid_i同步到clk_o(单比特慢时钟域到快时钟域的同步),我顺便也把data_in同步到clk_o时钟域来了(这里是多bit,不知道可行不。,存疑,希望大佬看到的时候能答疑一下),再执行并转串的操作。
module patose(
input clk_i,
input clk_o,
input rst,
input [3:0]data_in,
input valid_i,
output reg data_out,
output reg valid_o
);
reg data_out_r;
reg [3:0]data_in_r;
reg [3:0]data_in_r1;
reg valid_i_r;
reg valid_i_r1;
reg state;
reg [3:0]data_in_r2;
reg [2:0]cnt;
always@(posedge clk_o)
if(rst)
begin
data_in_r<=0;
data_in_r1<=0;
valid_i_r<=0;
valid_i_r1<=0;
end
else
begin
data_in_r<=data_in;
data_in_r1<=data_in_r;
valid_i_r<=valid_i;
valid_i_r1<=valid_i_r;
end
always@(posedge clk_o or posedge rst)
if(rst)
begin
data_out<=0;
valid_o<=0;
state<=1'b0;
cnt<=0;
data_in_r2<=0;
valid_o<=0;
end
else
begin
case(state)
1'b0:if(valid_i_r1)
begin
data_out<=data_in_r1[0];
data_in_r2<={1'b0,data_in_r1[3:1]};
state<=2'b01;
cnt<=cnt+1;
valid_o<=1;
end
else
begin
data_out<=0;
valid_o<=0;
state<=1'b0;
cnt<=0;
data_in_r2<=0;
valid_o<=0;
end
1'b1: begin
if(cnt==4)begin
data_out<=data_in_r2[0];
data_in_r2<={1'b0,data_in_r2[3:1]};
cnt<=0;
state<=2'b00;
valid_o<=0;
end
else
begin
cnt<=cnt+1;
data_out<=data_in_r2[0];
data_in_r2<={1'b0,data_in_r2[3:1]};
valid_o<=valid_o;
end
end
endcase
end
endmodule
2、仿真文件
module patose_tst();
reg clk_i;
reg clk_o;
reg rst ;
reg [3:0]data_in;
reg valid_i ;
wire data_out ;
wire valid_o ;
patose U_patose(
. clk_i (clk_i ),
. clk_o (clk_o ),
. rst (rst ),
. data_in (data_in ),
. valid_i (valid_i ),
. data_out(data_out),
. valid_o (valid_o )
);
initial
begin
data_in=0;
clk_i=1;
clk_o=1;
rst=1;
#40 rst=0;
#40 data_in=4'b1011;
valid_i=1;
#40 data_in=4'b1001;
#40 valid_i=0;
end
always #20 clk_i=~clk_i;
always #5 clk_o=~clk_o;
endmodule
3、修正后的代码
(1)
先说一下思路,这个题的考点:第一是跨时钟域;第二是并转串;跨时钟域的话我是只对valid_i进行跨时钟域,数据的处理我其实还是不知道对不对,后面知道怎么做了再来更新。并转串其实比较常规,看代码就能理解。
还有一个隐含的条件就是:数据可能是连续来的,也可能不是连续来的。
那,怎么判断每一次来的4bit数据传输开始和结束呢?传输结束比较好判断,就是传输开始后用一个计数器即可。开始怎么判断呢?大家可以思考一下这里。
我是这样想的:才开始想用valid_i_r1的上升沿来判断,但是呢如果数据是连续来的这个办法就不可行了,这只能适用于数据是断开的情况。后面我发现一个特征,就是每次4bit都会有clk_i出现上升沿,所以开始传输的条件可以为clk_i出现上升沿且valid_i有效。这个是大致的一个思考过程,大家可以画图帮助理解。
(2)设计文件
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/07/31 19:27:59
// Design Name:
// Module Name: patose
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module patose(
input clk_i,
input clk_o,
input rst,
input [3:0]data_in,
input valid_i,
output reg data_out,
output reg valid_o
);
reg data_out_r;
reg [3:0]data_in_r;
reg [3:0]data_in_r1;
reg valid_i_r;
reg valid_i_r1;
reg state;
reg [3:0]data_in_r2;
reg [2:0]cnt;
//wire posclk_i_flg;//clk_i出现上升沿的标志
reg posclk_i_flg;
reg clk_i_r;
reg clk_i_r1;
always@(posedge clk_o)
if(rst)
begin clk_i_r<=0;clk_i_r1<=0;end
else begin clk_i_r<=clk_i;clk_i_r1<=clk_i_r;end
//assign posclk_i_flg=(clk_i_r==1&&clk_i_r1==0)?1:0;
always@(posedge clk_o)
if(rst)
posclk_i_flg<=0;
else if(clk_i_r==1&&clk_i_r1==0)
posclk_i_flg<=1;
else posclk_i_flg<=0;
always@(posedge clk_o)
if(rst)
begin
//data_in_r<=0;
//data_in_r1<=0;
valid_i_r<=0;
valid_i_r1<=0;
end
else
begin
//data_in_r<=data_in;
//data_in_r1<=data_in_r;
valid_i_r<=valid_i;
valid_i_r1<=valid_i_r;
end
always@(posedge clk_o)
if(rst)
begin
data_out<=0;
valid_o<=0;
state<=1'b0;
cnt<=0;
data_in_r2<=0;
valid_o<=0;
end
else
begin
case(state)
1'b0:if(valid_i_r1&&posclk_i_flg)
begin
//data_out<=data_in_r1[0];
data_out<=data_in[0];
//data_in_r2<={1'b0,data_in_r1[3:1]};
data_in_r2<={1'b0,data_in[3:1]};
state<=2'b01;
cnt<=cnt+1;
valid_o<=1;
end
else
begin
data_out<=0;
valid_o<=0;
state<=1'b0;
cnt<=0;
data_in_r2<=0;
valid_o<=0;
end
1'b1: begin
if(cnt==4)begin
data_out<=data_in_r2[0];
data_in_r2<={1'b0,data_in_r2[3:1]};
cnt<=0;
state<=1'b0;
valid_o<=0;
end
else
begin
cnt<=cnt+1;
data_out<=data_in_r2[0];
data_in_r2<={1'b0,data_in_r2[3:1]};
valid_o<=valid_o;
end
end
endcase
end
endmodule
(3)仿真文件:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/07/31 19:29:38
// Design Name:
// Module Name: patose_tst
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module patose_tst();
reg clk_i;
reg clk_o;
reg rst ;
reg [3:0]data_in;
reg valid_i ;
wire data_out ;
wire valid_o ;
patose U_patose(
. clk_i (clk_i ),
. clk_o (clk_o ),
. rst (rst ),
. data_in (data_in ),
. valid_i (valid_i ),
. data_out(data_out),
. valid_o (valid_o )
);
initial
begin
data_in=0;
clk_i=1;
clk_o=1;
valid_i=0;
rst=1;
#40 rst=0;
#40 data_in=4'b1011;
valid_i=1;
#40 data_in=4'b1001;
#40 valid_i=0;
#80 valid_i=1;
data_in=4'b1101;
#40 valid_i=0;
#80 valid_i=1;
data_in=4'b1001;
#40 valid_i=0;
end
always #20 clk_i=~clk_i;
always #2 clk_o=~clk_o;
endmodule
(3)波形图
正确的波形(输出时钟频率为输入时钟频率10倍的情况)
之前的仿真文件设置的输出时钟频率刚好为输入时钟频率的4倍,隐藏了我的设计错误。错误现象如下:
大家在阅读的过程中,如果发现不对的地方,欢迎大家批评指正。