/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倍,隐藏了我的设计错误。错误现象如下:

大家在阅读的过程中,如果发现不对的地方,欢迎大家批评指正。