Verilog手撕代码(5)串转并、并转串

串转并的使用环境非常多,在接口处用到最多,在某些模块的输入仅允许串行输入时,而总线上的数时并行的,那就需要通过串并转换,把并行的数据转换成串行的数据,包括在输出的时候,需要把串行的数据转换成并行的数据,才能送到总线中,使用方法都是用一个计数器来计数实现 。

串转并

常规转换

用一个计数器count,每输入8个数,就输出一次,每周期dout_temp左移一位,然后再将输入的1bit串行据存入dout_temp的最低位。

module serial_to_parallel(
	input clk,
	input rst_n,
	input din_serial,
	input din_valid,
	output reg [7:0]dout_parallel,
	output reg dout_valid
);

	reg[7:0]din_tmp;
	reg[3:0]cnt;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt <= 0;
		else if(din_valid)
			cnt <= (cnt == 4'd8)?0:cnt+1'b1;
		else	
			cnt <= 0;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			din_tmp <= 8'b0;
		else if(din_valid && cnt <= 4'd7)
			din_tmp <= {din_tmp[6:0],din_serial};
	
	end 
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			dout_valid <= 1'b0;
			dout_parallel <= 8'b0;
		end
		else if(cnt == 4'd8)begin
			dout_valid <= 1'b1;
			dout_parallel <= din_tmp;
		end
		else begin
			dout_valid <= 1'b0;
		end
	end 
	
endmodule

Testbench:

module serial_to_parallel_tb();
	reg clk,rst_n;
	reg din_serial,din_valid;
	wire dout_valid;
	wire[7:0]dout_parallel;
 
	always #5 clk = ~clk;
 
	initial begin
		clk  <= 1'b0;
		rst_n <= 1'b0;
		#15
		rst_n <= 1'b1;
		din_valid  <= 1'b1;
		din_serial <= 1'b1; #10
		din_serial <= 1'b1; #10
		din_serial <= 1'b1; #10
		din_serial <= 1'b1; #10
	
		din_serial <= 1'b0; #10
		din_serial <= 1'b0; #10
		din_serial <= 1'b0; #10
		din_serial <= 1'b0; #10
		din_valid  <= 1'b0;
		#30
		din_valid  <= 1'b1;
		din_serial <= 1'b1; #10
		din_serial <= 1'b1; #10
		din_serial <= 1'b0; #10
		din_serial <= 1'b0; #10
	
		din_serial <= 1'b0; #10
		din_serial <= 1'b0; #10
		din_serial <= 1'b1; #10
		din_serial <= 1'b1; #20
		din_valid  <= 1'b0;
		#50
		$stop;
	end 
	serial_to_parallel u0(
    .clk           (clk)           ,
    .rst_n          (rst_n)          ,
    .din_serial    (din_serial)    ,
    .dout_parallel (dout_parallel) ,
    .din_valid     (din_valid)     ,
    .dout_valid    (dout_valid)
	);  
endmodule

仿真结果:
在这里插入图片描述

结合握手机制的串转并

实现串并转换电路,输入端输入单bit数据,每当本模块接收到6个输入数据后,输出端输出拼接后的6bit数据。本模块输入端与上游的采用valid-ready双向握手机制,输出端与下游采用valid-only握手机制。数据拼接时先接收到的数据放到data_b的低位。

电路的接口如下图所示。valid_a用来指示数据输入data_a的有效性,valid_b用来指示数据输出data_b的有效性;ready_a用来指示本模块是否准备好接收上游数据,本模块中一直拉高;clk是时钟信号;rst_n是异步复位信号。

在这里插入图片描述

在这里插入图片描述
代码:

module s_to_p(
	input clk,
	input rst_n,
	input valid_a,
	input data_a,
	
	output reg ready_a,
	output reg valid_b,
	output reg [5:0]data_b
);
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		ready_a <= 1'b0;
	else
		ready_a <= 1'b1;
end

re[2:0]cnt;
always@(posedge clk or negeedge rst_n)begin
	if(!rst_n)
		cnt <= 3'd0;
	else if(valid_a && ready_a)
		cnt <= cnt == 3'd5 ? 3'd0 : cnt + 1'b1;
	else
		cnt <= cnt;
end

reg [5:0]data_r;
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		data_r <= 6'd0;
	esle if(valid_a && ready_a)
		data_r <= {data_a,data_r[5:1]};
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		valid_b <= 1'b0;
		data_b <= 6'd0;
	end
	else if(cnt == 3'd5)begin
		data_b <= {data_a,data_r[5:1]};
		valid_b <= 1'b1;
	end
	else
		valid_b <= 1'b0;
end

endmodule

并转串

并转串的原理和串转并差不多,都是用一个计数器来技术,根据串行和并行之间的关系来决定计数器计数的上下限。

在输入数据有效的时候din_valid为高时,把输入的数据寄存一下,寄存到din_parallel_temp中,然后再用一个计数器去检测输出,当计数器处在1~8的范围内时,让输出的串行数据等于输入的并行数据的最高位,然后每周期让并行数据左移一bit。这样,八周期后,就实现了并转串。

代码:

module parallel_to_serial(
	input clk,
	input rst_n,
	input [7:0] din_parallel,
	input din_valid,
	output reg dout_serial,
	output reg dout_valid
);

	reg[7:0]din_parallel_tmp;
	reg [3:0]cnt;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt <= 0;
		else if(din_valid)
			cnt <= cnt + 1'b1;
		else
			cnt <= 0;		
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			din_parallel_tmp <= 8'b0;	
			dout_serial <= 1'b0;
			dout_valid <= 1'b0;
		end
		else if(din_valid && cnt == 0)begin
			din_parallel_tmp <= din_parallel;
		end
		else if((cnt >= 4'd1) && (cnt <= 4'd8))begin
			dout_serial <= din_parallel_tmp[7];
			din_parallel_tmp <= din_parallel_tmp << 1;
			dout_valid <= 1'b1;
		end
		else begin
			dout_serial <= 1'b0;
			dout_valid <= 1'b0;
		end
	end
	
endmodule

Testbench:

module parallel_to_serial_tb();
reg clk, rst_n;
reg [7:0] din_parallel;
reg din_valid;
 
wire dout_valid,dout_serial;
 
always #5 clk = ~clk;
 
initial begin
    clk <= 1'b0;
    rst_n <= 1'b0;
    #15
    rst_n <= 1'b1;   
    din_valid <= 1'b1;
    din_parallel <= 8'b11110000;
    #80
    din_valid <= 1'b0;
    #40
    din_valid <= 1'b1;
    din_parallel <= 8'b10100011;
    #80
    din_valid <= 1'b0;
    #50
    $stop();
end
parallel_to_serial u_parallel_to_serial(
    .clk           (clk)          ,    
    .rst_n         (rst_n)         ,    
    .din_parallel  (din_parallel) ,
    .din_valid     (din_valid)    ,
    .dout_serial   (dout_serial)  ,
    .dout_valid    (dout_valid)
);
endmodule

仿真结果:
在这里插入图片描述

### Verilog 串行实现方法 #### 方法概述 行到串行换器是一种常见的硬件模块,在通信接口和其他应用中广泛使用。该过程涉及将多个输入位组合成单个输出流,通常通过移位寄存器来完成。 #### 设计思路 设计一个简单的行到串行换电路时,主要考虑以下几个方面: - **同步控制**:利用时钟信号 `clk` 和复位信号 `reset` 来管理状态变化。 - **使能机制**:引入使能信号 `en` 控制何时加载新的行数据以及启动传输操作。 - **存储结构**:采用寄存器数组保存当前待发送的数据序列。 - **移位逻辑**:每当接收到有效脉冲时,向左移动一位将最高位置于输出端口上。 下面是一个基于上述原则的具体实例化方案[^3]。 ```verilog // 定义模块参数和I/O端口 module ParallelToSerial( input wire clk, input wire reset_n, //低电平有效的异步复位 input wire [15:0] din, //16比特宽的行输入 input wire load_en, //装载新数据允许标志 output reg dout //串行输出线 ); reg [15:0] shift_reg; //用于暂存即将被化的数据 always @(posedge clk or negedge reset_n) begin : proc_shift_out if (!reset_n) //当检测到复位请求时清零内部寄存器 shift_reg <= 16'b0; else if (load_en) //如果处于加载模式,则更新shift register中的内容 shift_reg <= din; else //正常工作状态下不断右移直到全部送出为止 shift_reg <= {shift_reg[14:0], 1'b0}; end assign dout = shift_reg[15]; //始终取最左边的一位作为最终输出 endmodule ``` 此段代码展示了如何创建一个能够接受16位宽度行输入通过连续触发事件逐步将其化为单一比特流输出的功能实体。值得注意的是这里采用了负边沿敏感型复位(`negedge`)以确保更稳健的操作特性;同时为了简化说明仅保留了一个方向上的移位动作即从高位到底位依次发出各个组成单元。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值